Introduction

Goal: to compare the difference in gene expression between sensitive and resistant cell lines of three different lines of tumors.

RNA-seq was run for three isogenic tumor cell lines (PEO1, PEO4, and PEO6) Sample preparation was performed in Dr. Lang’s lab. Preparation of cells and RNA extraction was done by Kendra, Josie, and Sydney. RNA seq library prep was done by Kristen. Analysis done by Ryan.

Biological Questions:

Inputs

Inputs consisted of:

Define functions to make an interactive plots from revigo data (code based off shiny_rrvgo)

Define upset plot helper functions (code from https://github.com/hms-dbmi/UpSetR/issues/85#issuecomment-415480954)

DESeq2 - setup

Read in metadata table

sensitive_resistant_pairs <- c("OVCAR3A_vs_OVCAR3", "OVCAR3B_vs_OVCAR3", "OVCAR4A_vs_OVCAR4", "OVCAR4B_vs_OVCAR4", "PEA2_vs_PEA1", "PEO6_vs_PEO1", "PEO4_vs_PEO1")

isolines <- data.frame(pair = sensitive_resistant_pairs,
                       isoline = c("OVCAR3",
                                   "OVCAR3",
                                   "OVCAR4",
                                   "OVCAR4",
                                   "PEA",
                                   "PEO",
                                   "PEO"))
metadata.all <- as.data.frame(read.table("deseq/metadata.csv", sep = ",", header = TRUE))
rownames(metadata.all) <- metadata.all$ShortName

# Should put this in the metadata file, but just doing this to save time
for (row in 1:nrow(metadata.all)) {
    isogenicRank <- 1
    resistant <- 0
    if (metadata.all$CellLine[row] %in% list("OVCAR3A", "OVCAR4A", "PEA2", "PEO4")) {
      isogenicRank <- 2
      resistant <- 1
    } else if (metadata.all$CellLine[row] %in% list("OVCAR3B", "OVCAR4B", "PEO6")) {
      isogenicRank <- 3
      resistant <- 1
    }
    metadata.all$IsogenicRank[row] <- isogenicRank
    metadata.all$Resistant[row] <- resistant
}
metadata.all

Load count matrix

countmatrix <- as.matrix(read.table("../star_salmon/salmon.merged.gene_counts.tsv", sep = "\t", header = TRUE))
row.names(countmatrix) <- countmatrix[, "gene_name"]
countmatrix <- countmatrix[, 3:ncol(countmatrix)]
countmatrix.all <- matrix(as.numeric(countmatrix), ncol = ncol(countmatrix), dimnames = list(rownames(countmatrix), colnames(countmatrix)))
countmatrix.all <- round(countmatrix.all)
countmatrix.all <- countmatrix.all[, metadata.all$LongName]
colnames(countmatrix.all) <- metadata.all$ShortName[match(colnames(countmatrix.all), metadata.all$LongName)] # Renames the Salmon countmatrix using formatted short name
as.data.frame(countmatrix.all)

Load TPM matrix

TPM <- as.matrix(read.delim("../star_salmon/salmon.merged.gene_tpm.tsv", sep="\t", row.names="gene_id"))
TPM <- TPM[,-1]
TPM <- matrix(as.numeric(TPM), ncol = ncol(TPM), dimnames = list(rownames(TPM), colnames(TPM)))
TPM.log <- log(TPM + 1)
as.data.frame(TPM.log)
colnames(TPM.log) <- metadata.all$ShortName

PCA plot

Running DESeq on all of the celllines together to get normalized data.

dds.all = DESeqDataSetFromMatrix(
  countData = countmatrix.all,
  colData = metadata.all,
  design = ~ Replicate + CellLine
)
dds.all = DESeq(dds.all)
save(dds.all, file = str_interp("deseq/Rdata/all_celllines_dds.RData"))
# load(str_interp("deseq/Rdata/all_celllines_dds.RData"))

Transform data with a variance stabilized transformation

vsd.all = assay(vst(dds.all))
meanSdPlot(vsd.all)

PCA plot formatted for paper

vsd.all.fullobj = vst(dds.all)
pcaData <- plotPCA(vsd.all.fullobj, intgroup=c("CellLine", "Resistant"), returnData=TRUE)
pcaData$Resistance <- ifelse(pcaData$Resistant == 0, "Sensitive", "Resistant")
percentVar <- round(100 * attr(pcaData, "percentVar"))
myColors <- c("#76AB7E", "#63E678", "#0E9D1F", "#7B87FD", "#1D32FB", "#1452FB", "#E87426", "#DAC426", "#8769E7", "#E019E7", "#9B19E7")
names(myColors) <- levels(pcaData$CellLine)
colScale <- scale_colour_manual(name = "CellLine",values = myColors)
pca <- ggplot(pcaData, aes(PC1, PC2, color=CellLine, shape=Resistance, label="")) +
  geom_point(size=3) +
  geom_text(hjust=0, vjust=0) +
  xlab(paste0("PC1: ",percentVar[1],"% variance")) +
  ylab(paste0("PC2: ",percentVar[2],"% variance")) +
  coord_fixed() +
  theme_classic() + 
  colScale
pca
ggsave("deseq/output/pca_all_celllines_formatted.svg",
       plot = last_plot(),
       device = "svg",
       width = 6,
       units = "in")
Saving 6 x 4.51 in image

DESeq2 - Analysis of each Sensitive/Resistant pair

for (pair in sensitive_resistant_pairs) {
  split <- strsplit(pair, "_vs_")
  cont <- split[[1]][2]
  exp <- split[[1]][1]
  
  # Creates another notebook that shares the same environment
  outFile <- str_interp("generated-notebooks/deseq-analysis-${pair}.html")
  print(str_interp("Creating notebook for ${exp} vs ${cont}"))
  rmarkdown::render('deseq/single-cellline-vs-control.Rmd', 
                    output_file = outFile, 
                    params = list(controlCellLine = cont, 
                                  experimentalCellLine = exp,
                                  countmatrix.all = countmatrix.all,
                                  metadata.all = metadata.all))
}

Combine results across pairs

Combine differential gene data

full_results_genes <- data.frame()
for (pair in sensitive_resistant_pairs) {
  pair_results_file <- str_interp("deseq/output/${pair}_deseq_results.csv")
  pair_results <- as.data.frame(read.csv(pair_results_file, sep = ",", header = TRUE, row.names = 1))
  pair_results_formatted <- pair_results[, c("padj", "log2FoldChange")]
  colnames(pair_results_formatted) <- c(str_interp("${pair}_padj"), str_interp("${pair}_l2fc"))
  pair_results_formatted$gene <- rownames(pair_results_formatted)

  if (ncol(full_results_genes) == 0) {
    full_results_genes = pair_results_formatted
  } else {
    full_results_genes <- merge(full_results_genes, pair_results_formatted, by = "gene", all = TRUE)
  }
}

rownames(full_results_genes) <- full_results_genes$gene
full_results_genes <- subset(full_results_genes, select = -c(gene))

for (row in 1:nrow(full_results_genes)){
  num_pairs_sig_regulated = 0
  num_pairs_sig_downregulated = 0
  num_pairs_sig_upregulated = 0
  for (pair in sensitive_resistant_pairs) {
    padj = full_results_genes[row, str_interp("${pair}_padj")]
    l2fc = full_results_genes[row, str_interp("${pair}_l2fc")]
    if (!is.na(padj) && padj < 0.05) {
      if (l2fc < 0) {
        num_pairs_sig_downregulated = num_pairs_sig_downregulated + 1
      } else if (l2fc > 0) {
        num_pairs_sig_upregulated = num_pairs_sig_upregulated + 1
      }
    }
  }
  full_results_genes[row, "num_pairs_sig_up_regulated"] = num_pairs_sig_upregulated
  full_results_genes[row, "num_pairs_sig_down_regulated"] = num_pairs_sig_downregulated
  full_results_genes[row, "num_pairs_sig_regulated"] = num_pairs_sig_upregulated + num_pairs_sig_downregulated
}

full_results_genes = full_results_genes %>% 
  relocate(num_pairs_sig_down_regulated)%>% 
  relocate(num_pairs_sig_up_regulated) %>% 
  relocate(num_pairs_sig_regulated)
full_results_genes = full_results_genes[order(full_results_genes$num_pairs_sig_regulated, decreasing = TRUE),]

print(full_results_genes)
write.csv(full_results_genes, file = "deseq/output/differential_gene_expression_all_sensitive_resistant_pairs.csv")

Combine differential pathway data

### Combine results across pairs
full_results_pathways <- data.frame()
for (pair in sensitive_resistant_pairs) {
  pair_up_pathways_file <- str_interp("deseq/output/${pair}_significantly_upregulated_pathways.csv")
  pair_up_pathways <- as.data.frame(read.csv(pair_up_pathways_file, sep = ",", header = TRUE, row.names = 1))
  
  pair_down_pathways_file <- str_interp("deseq/output/${pair}_significantly_downregulated_pathways.csv")
  pair_down_pathways <- as.data.frame(read.csv(pair_down_pathways_file, sep = ",", header = TRUE, row.names = 1))
  
  pair_pathways = rbind(pair_up_pathways, pair_down_pathways)
  pair_pathways$ID = rownames(pair_pathways)
  pair_pathways_formatted = pair_pathways[, c("ID", "Description", "p.adjust", "NES")]

  colnames(pair_pathways_formatted) <- c("ID", "Description", str_interp("${pair}_padj"), str_interp("${pair}_NES"))
  
  if (ncol(full_results_pathways) == 0) {
    full_results_pathways = pair_pathways_formatted
  } else {
    full_results_pathways <- merge(full_results_pathways, pair_pathways_formatted, by = c("ID", "Description"), all = TRUE)
  }
}

rownames(full_results_pathways) <- full_results_pathways$ID
full_results_pathways <- subset(full_results_pathways, select = -c(ID))

for (row in 1:nrow(full_results_pathways)){
  num_pairs_sig_regulated = 0
  num_pairs_sig_downregulated = 0
  num_pairs_sig_upregulated = 0
  for (pair in sensitive_resistant_pairs) {
    padj = full_results_pathways[row, str_interp("${pair}_padj")]
    NES = full_results_pathways[row, str_interp("${pair}_NES")]
    if (!is.na(padj) && padj < 0.05) {
      if (NES < 0) {
        num_pairs_sig_downregulated = num_pairs_sig_downregulated + 1
      } else if (NES > 0) {
        num_pairs_sig_upregulated = num_pairs_sig_upregulated + 1
      }
    }
  }
  full_results_pathways[row, "num_pairs_sig_up_regulated"] = num_pairs_sig_upregulated
  full_results_pathways[row, "num_pairs_sig_down_regulated"] = num_pairs_sig_downregulated
  full_results_pathways[row, "num_pairs_sig_regulated"] = num_pairs_sig_upregulated + num_pairs_sig_downregulated
}

full_results_pathways = full_results_pathways %>% 
  relocate(num_pairs_sig_down_regulated)%>% 
  relocate(num_pairs_sig_up_regulated) %>% 
  relocate(num_pairs_sig_regulated)
full_results_pathways = full_results_pathways[order(full_results_pathways$num_pairs_sig_regulated, decreasing = TRUE),]

print(full_results_pathways)
write.csv(full_results_pathways, file = "deseq/output/differential_pathways_all_sensitive_resistant_pairs.csv")

Plot differential pathways

Use REVIGO to cluster pathways


similarity_matrix = calculateSimMatrix(rownames(full_results_pathways),
                                       orgdb = "org.Hs.eg.db",
                                       ont = "BP",
                                       method = "Rel")
Warning in GOSemSim::godata(orgdb, ont = ont, keytype = keytype) :
  use 'annoDb' instead of 'OrgDb'
preparing gene to GO mapping data...
preparing IC data...
Warning in calculateSimMatrix(rownames(full_results_pathways), orgdb = "org.Hs.eg.db",  :
  Removed 2 terms that were not found in orgdb for BP
scores = rep(1, ncol(similarity_matrix))
names(scores) = colnames(similarity_matrix)
reducedTerms = reduceSimMatrix(similarity_matrix,
                               scores,
                               threshold = 0.8,
                               orgdb = "org.Hs.eg.db")
'select()' returned 1:many mapping between keys and columns

Plot consistently differential pathways for all celllines in one plot

darken_color <- function(color, factor = 0.5) {
  # Convert color to RGB
  rgb_vals <- col2rgb(color) / 255
  # Darken each RGB channel
  darkened_vals <- rgb_vals * factor
  # Ensure values are within valid range [0, 1]
  darkened_vals <- pmin(pmax(darkened_vals, 0), 1)
  # Convert back to hexadecimal color
  darkened_color <- rgb(darkened_vals[1], darkened_vals[2], darkened_vals[3], maxColorValue = 255)
  return(darkened_color)
}


format_pathway_results = full_results_pathways
# Subset to terms able to be clustered
format_pathway_results = format_pathway_results[rownames(format_pathway_results) %in% rownames(reducedTerms),]
format_pathway_results = format_pathway_results[,- which(colnames(format_pathway_results) %in% c("num_pairs_sig_down_regulated", "num_pairs_sig_up_regulated"))]

# Change formatting of column names so that it is easier for pivot function below
colnames(format_pathway_results) = colnames(format_pathway_results) %>%
  map_chr(\(x) sub("_(?=[^_]*$)", "-", x, perl = TRUE))

# Set parent terms
format_pathway_results$parentPathwayId = rownames(format_pathway_results) %>%
  map_chr(\(x) reducedTerms[rownames(reducedTerms) == x, "parent"])
format_pathway_results$parentPathwayDescription = format_pathway_results$parentPathwayId %>%
  map_chr(\(x) paste0(format_pathway_results[rownames(format_pathway_results) == x, "Description"], " (", nrow(format_pathway_results[format_pathway_results$parentPathwayId == x,]), ")"))

pivot_cols = colnames(format_pathway_results)
pivot_cols = pivot_cols[!(pivot_cols %in% c("Description", "num_pairs_sig-regulated", "parentPathwayId", "parentPathwayDescription"))]
plot_data = pivot_longer(data = format_pathway_results,
                         cols = pivot_cols,
                         names_to = c("celllinePair", ".value"),
                         names_sep = "-")
Warning: Using an external vector in selections was deprecated in tidyselect 1.1.0.
ℹ Please use `all_of()` or `any_of()` instead.
  # Was:
  data %>% select(pivot_cols)

  # Now:
  data %>% select(all_of(pivot_cols))

See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
# Score the term categories by combining scores across all child pathways and all cellline pairs
plot_data$score = plot_data$NES
# plot_data$score = -log10(plot_data$padj) * plot_data$NES
plot_data = plot_data %>%
  group_by(parentPathwayId) %>%
  mutate(category_score = mean(score, na.rm = TRUE))

# Order terms by the score of the parent pathway
plot_data = plot_data %>% arrange(category_score, score)
plot_data$parentPathwayDescription <- factor(plot_data$parentPathwayDescription, levels = unique(plot_data$parentPathwayDescription))

# Shift NES so that the -1 to 1 region doesn't go unused
if (any(abs(plot_data$NES)) < 1) {
  print("ERROR: This plot will be messed up because we assumed novalues of NES between -1 and 1.")
}
Warning in any(abs(plot_data$NES)) :
  coercing argument of type 'double' to logical
# plot_data$shift_NES = plot_data$NES %>%
#   map_dbl(\(x) ifelse(x > 1, x - 1, ifelse(x < -1, x + 1, x)))

ggplot(plot_data, aes(x = NES, y = parentPathwayDescription, size = -log10(padj), color = celllinePair)) +
  geom_point(position = position_jitter(height = 0.1), alpha = 0.5, layer = "above") +  # Add jitter and reduce point transparency
  scale_size_continuous(range = c(1, 5)) +
  # Coloring by in vivo/in vitro resistance (OVCARs both in vitro; PEO and PEAs in vivo)
  scale_color_manual(values = c("#F71300", "#E7CF00",
                                "#F55AFA", "#BD8161",
                                "#11DBCC", "#0E58E4",
                                "#14B70A")) +
  geom_hline(yintercept = seq_along(levels(plot_data$parentPathwayDescription)), color = "gray", size = 0.1, layer = "below") +
  geom_vline(xintercept = 0, linetype = "dashed", color = "black") +

  labs(x = 'Normalized Enrichment Score', y = 'Pathway Category (# pathways)', color = 'Cellline Pair') +
  ggtitle('Pathway regulation in resistant celllines (as compared to corresponding sensitive cellline)') +
  theme(panel.grid.minor.x = element_blank(),
        panel.grid.major.x = element_blank(),
        panel.grid.minor.y = element_blank(),
        panel.grid.major.y = element_blank()) +
  theme_classic()
Warning in geom_point(position = position_jitter(height = 0.1), alpha = 0.5,  :
  Ignoring unknown parameters: `layer`
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
Warning in geom_hline(yintercept = seq_along(levels(plot_data$parentPathwayDescription)),  :
  Ignoring unknown parameters: `layer`
Warning: Removed 3191 rows containing missing values or values outside the scale range (`geom_point()`).

ggsave("deseq/output/differential_pathways_all_celllines_similarity_threshold_0.8.svg",
       plot = last_plot(),
       device = "svg",
       width = 15,
       height = 10,
       units = "in")
Warning: Removed 3191 rows containing missing values or values outside the scale range (`geom_point()`).
## plot in vitro resistance only
ggplot(plot_data[plot_data$celllinePair %in% c("OVCAR4B_vs_OVCAR4", "OVCAR4A_vs_OVCAR4", "OVCAR3A_vs_OVCAR3", "OVCAR3B_vs_OVCAR3"),], aes(x = NES, y = parentPathwayDescription, size = -log10(padj), color = celllinePair)) +
  geom_point(position = position_jitter(height = 0.1), alpha = 0.5, layer = "above") +  # Add jitter and reduce point transparency
  scale_size_continuous(range = c(1, 5)) +
  scale_color_manual(values = c("#F71300", "#E7CF00",
                                "#F55AFA", "#BD8161")) +
  geom_hline(yintercept = seq_along(levels(plot_data$parentPathwayDescription)), color = "gray", size = 0.1, layer = "below") +
  geom_vline(xintercept = 0, linetype = "dashed", color = "black") +

  labs(x = 'Normalized Enrichment Score', y = 'Pathway Category (# pathways)', color = 'Cellline Pair') +
  ggtitle('Pathway regulation in in-vitro derived resistant celllines') +
  theme(panel.grid.minor.x = element_blank(),
        panel.grid.major.x = element_blank(),
        panel.grid.minor.y = element_blank(),
        panel.grid.major.y = element_blank()) +
  theme_classic()
Warning in geom_point(position = position_jitter(height = 0.1), alpha = 0.5,  :
  Ignoring unknown parameters: `layer`
Warning in geom_hline(yintercept = seq_along(levels(plot_data$parentPathwayDescription)),  :
  Ignoring unknown parameters: `layer`
Warning: Removed 1901 rows containing missing values or values outside the scale range (`geom_point()`).

## plot in vivo resistance only
ggplot(plot_data[plot_data$celllinePair %in% c("PEO4_vs_PEO2", "PEO6_vs_PEO2", "PEA2_vs_PEA1"),], aes(x = NES, y = parentPathwayDescription, size = -log10(padj), color = celllinePair)) +
  geom_point(position = position_jitter(height = 0.1), alpha = 0.5, layer = "above") +  # Add jitter and reduce point transparency
  scale_size_continuous(range = c(1, 5)) +
  scale_color_manual(values = c("#11DBCC", "#0E58E4",
                                "#14B70A")) +
  geom_hline(yintercept = seq_along(levels(plot_data$parentPathwayDescription)), color = "gray", size = 0.1, layer = "below") +
  geom_vline(xintercept = 0, linetype = "dashed", color = "black") +

  labs(x = 'Normalized Enrichment Score', y = 'Pathway Category (# pathways)', color = 'Cellline Pair') +
  ggtitle('Pathway regulation in in-vivo derived resistant celllines') +
  theme(panel.grid.minor.x = element_blank(),
        panel.grid.major.x = element_blank(),
        panel.grid.minor.y = element_blank(),
        panel.grid.major.y = element_blank()) +
  theme_classic()
Warning in geom_point(position = position_jitter(height = 0.1), alpha = 0.5,  :
  Ignoring unknown parameters: `layer`
Warning in geom_hline(yintercept = seq_along(levels(plot_data$parentPathwayDescription)),  :
  Ignoring unknown parameters: `layer`
Warning: Removed 439 rows containing missing values or values outside the scale range (`geom_point()`).

Plot differential pathways

Plot shows how the spread of significantly differential pathways across sensitive/resistant pairs.

plot_data = full_results_pathways[,c("num_pairs_sig_up_regulated", "num_pairs_sig_down_regulated", "Description")]
print(plot_data)
# Orders primarily by how many more pairs were sig up than sig down regulated
# Orders secondarily by how few pairs were regulated in the non-dominant direction
# This ordering does the following: (7, 0) > (6, 0) > (5, 0) > (6, 1) > (7, 2)
plot_data <- plot_data %>%
  mutate(order = ifelse(num_pairs_sig_up_regulated > num_pairs_sig_down_regulated,
                        num_pairs_sig_up_regulated - num_pairs_sig_down_regulated * 1.01,
                        -1 * (num_pairs_sig_down_regulated - num_pairs_sig_up_regulated * 1.01)))

plot_data <- plot_data %>%
  arrange(desc(order))

plot_data = plot_data %>%
  mutate(panel = ifelse(num_pairs_sig_up_regulated > num_pairs_sig_down_regulated,
                        "overall upregulated",
                        ifelse(num_pairs_sig_down_regulated == num_pairs_sig_up_regulated,
                               "evenly up and down regulated",
                               "overall downregulated")))
plot_data$panel = factor(plot_data$panel, levels = c("overall upregulated", "evenly up and down regulated", "overall downregulated"))

# organize the x coordinates based on panel
plot_data$left_placement = 0:(nrow(plot_data) - 1)
equal_panel_start = min(plot_data[plot_data$panel == "evenly up and down regulated", "left_placement"])
plot_data[plot_data$panel == "evenly up and down regulated",]$left_placement = plot_data[plot_data$panel == "evenly up and down regulated",]$left_placement - equal_panel_start
down_panel_start = min(plot_data[plot_data$panel == "overall downregulated", "left_placement"])
plot_data[plot_data$panel == "overall downregulated",]$left_placement = plot_data[plot_data$panel == "overall downregulated",]$left_placement - down_panel_start

plot_data$center_placement = plot_data$left_placement + 0.5

# Create bar plot
ggplot(plot_data) +
  facet_wrap(~panel, ncol = 3) +
  geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = 1), stat = "identity") +
  geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = 1), stat = "identity") +
  scale_y_continuous(limits = c(-max(plot_data$num_pairs_sig_down_regulated) -1, max(plot_data$num_pairs_sig_up_regulated)) +1) +
  theme_minimal() +  # Apply a minimal theme
  labs(x = "Pathways", y = "# resistant celllines showing significant regulation of pathway") + # Set axis labels
  scale_fill_manual(values = c("upregulation" = "green", "downregulation" = "red")) + # Set fill colors
  theme_classic() + 
  theme(panel.grid.minor.x = element_blank(),
      panel.grid.major.x = element_blank(),
      panel.grid.minor.y = element_blank(),
      panel.grid.major.y = element_blank()) +
  ggtitle('Pathway regulation in resistant celllines (as compared to corresponding sensitive cellline)')
Warning in geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated,  :
  Ignoring unknown aesthetics: width
Warning in geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated,  :
  Ignoring unknown aesthetics: width

  

ggsave("deseq/output/consistently_differential_pathways_all_celllines.svg",
       plot = last_plot(),
       device = "svg",
       width = 10,
       height = 5,
       units = "in")

Close up of consistently upregulated pathways

# End the plot between "categories" of pathways, including less than 100 pathways total
end_index = 1
for(i in 1:100) {
  if (plot_data$order[i] != plot_data$order[i+1]) {
    end_index = i
  }
}

plot_data_up = plot_data[1:end_index,]
plot_data_up$pathway = plot_data_up$Description

print(plot_data_up)

ggplot(plot_data_up) +
  geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = 1), stat = "identity") +
  geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = 1), stat = "identity") +
  geom_text(aes(x = center_placement, y = 1, label = pathway), size = 2, color = "black", angle = 90) +
  scale_y_continuous(limits = c(-max(plot_data_up$num_pairs_sig_down_regulated) -1, max(plot_data_up$num_pairs_sig_up_regulated)) +1) +
  theme_minimal() +  # Apply a minimal theme
  labs(x = "Pathways", y = "# resistant celllines showing significant regulation of pathway") + # Set axis labels
  scale_fill_manual(values = c("upregulation" = "green", "downregulation" = "red")) + # Set fill colors
  theme_classic() + 
  theme(panel.grid.minor.x = element_blank(),
      panel.grid.major.x = element_blank(),
      panel.grid.minor.y = element_blank(),
      panel.grid.major.y = element_blank()) +
  ggtitle('Zooming in on left hand side (extremely consistently upregulated pathways)')
Warning in geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated,  :
  Ignoring unknown aesthetics: width
Warning in geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated,  :
  Ignoring unknown aesthetics: width
ggsave("deseq/output/consistently_upregulated_pathways_all_celllines.svg",
       plot = last_plot(),
       device = "svg",
       width = 10,
       height = 5,
       units = "in")

Close up of consistently downregulated pathways

# End the plot between "categories" of pathways, including less than 100 pathways total
start_index = 1
for(i in nrow(plot_data):(nrow(plot_data) - 99)) {
  if (plot_data$order[i] != plot_data$order[i-1]) {
    start_index = i
  }
}

plot_data_down = plot_data[start_index:nrow(plot_data),]
plot_data_down$center_placement = plot_data_down$center_placement - start_index + 1 # Start at 0
plot_data_down$pathway = plot_data_down$Description

print(plot_data_down)

ggplot(plot_data_down) +
  geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = 1), stat = "identity") +
  geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = 1), stat = "identity") +
  geom_text(aes(x = center_placement, y = -1, label = pathway), size = 2, color = "black", angle = 90) +
  scale_y_continuous(limits = c(-max(plot_data_down$num_pairs_sig_down_regulated) -1, max(plot_data_down$num_pairs_sig_up_regulated)) +1) +
  theme_minimal() +  # Apply a minimal theme
  labs(x = "Pathways", y = "# resistant celllines showing significant regulation of pathway") + # Set axis labels
  scale_fill_manual(values = c("upregulation" = "green", "downregulation" = "red")) + # Set fill colors
  theme_classic() + 
  theme(panel.grid.minor.x = element_blank(),
      panel.grid.major.x = element_blank(),
      panel.grid.minor.y = element_blank(),
      panel.grid.major.y = element_blank()) +
  ggtitle('Zooming in on right hand side (extremely consistently downregulated pathways)')
Warning in geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated,  :
  Ignoring unknown aesthetics: width
Warning in geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated,  :
  Ignoring unknown aesthetics: width
ggsave("deseq/output/consistently_downregulated_pathways_all_celllines.svg",
       plot = last_plot(),
       device = "svg",
       width = 10,
       height = 5,
       units = "in")

Plot differential genes

Plot shows how the spread of significantly differential genes across sensitive/resistant pairs.

# formatted_gene_results = full_results_genes[,c("num_pairs_sig_up_regulated", "num_pairs_sig_down_regulated")]

plot_data = full_results_genes[,c("num_pairs_sig_up_regulated", "num_pairs_sig_down_regulated")]

# plot_data <- full_results_genes %>%
#   group_by(num_pairs_sig_up_regulated, num_pairs_sig_down_regulated) %>%
#   summarise(count = n())

# Orders primarily by how many more pairs were sig up than sig down regulated
# Orders secondarily by how few pairs were regulated in the non-dominant direction
# This ordering does the following: (7, 0) > (6, 0) > (5, 0) > (6, 1) > (7, 2)
plot_data <- plot_data %>%
  mutate(order = ifelse(num_pairs_sig_up_regulated > num_pairs_sig_down_regulated,
                        num_pairs_sig_up_regulated - num_pairs_sig_down_regulated * 1.01,
                        -1 * (num_pairs_sig_down_regulated - num_pairs_sig_up_regulated * 1.01)))

plot_data <- plot_data %>%
  arrange(desc(order))

plot_data = plot_data %>%
  mutate(panel = ifelse(num_pairs_sig_up_regulated > num_pairs_sig_down_regulated,
                        "overall upregulated",
                        ifelse(num_pairs_sig_down_regulated == num_pairs_sig_up_regulated,
                               "evenly up and down regulated",
                               "overall downregulated")))
plot_data$panel = factor(plot_data$panel, levels = c("overall upregulated", "evenly up and down regulated", "overall downregulated"))

# Add a small amount of color if not regulated
unregulated = plot_data$num_pairs_sig_up_regulated == 0 & plot_data$num_pairs_sig_down_regulated == 0
plot_data[unregulated,]$num_pairs_sig_up_regulated = 0.005
plot_data[unregulated,]$num_pairs_sig_down_regulated = -0.005

# organize the x coordinates based on panel
plot_data$left_placement = 0:(nrow(plot_data) - 1)
equal_panel_start = min(plot_data[plot_data$panel == "evenly up and down regulated", "left_placement"])
plot_data[plot_data$panel == "evenly up and down regulated",]$left_placement = plot_data[plot_data$panel == "evenly up and down regulated",]$left_placement - equal_panel_start
down_panel_start = min(plot_data[plot_data$panel == "overall downregulated", "left_placement"])
plot_data[plot_data$panel == "overall downregulated",]$left_placement = plot_data[plot_data$panel == "overall downregulated",]$left_placement - down_panel_start

plot_data$center_placement = plot_data$left_placement + 0.5

# next_placement = 0
# for(row in 1:nrow(plot_data)) {
#   new_placement = next_placement
#   plot_data[row, "left_placement"] = new_placement
#   next_placement = new_placement + plot_data[row, "count"]
# }

# Need the center of the bar for plotting purposes
# plot_data$center_placement = plot_data$left_placement + plot_data$count/2

print(plot_data)

# Create bar plot
ggplot(plot_data) +
  facet_wrap(~panel, ncol = 3, scales = "fixed") +
  geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = 1), stat = "identity") +
  geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = 1), stat = "identity") +
  # geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = count), stat = "identity") +
  # geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = count), stat = "identity") +
  scale_y_continuous(limits = c(-max(plot_data$num_pairs_sig_down_regulated) -1, max(plot_data$num_pairs_sig_up_regulated)) +1) +
  # Set y-axis limits
  # coord_flip() +  # Flip the coordinates to create a sideways plot
  theme_minimal() +  # Apply a minimal theme
  labs(x = "Genes", y = "# resistant celllines showing significant regulation of gene") + # Set axis labels
  scale_fill_manual(values = c("upregulation" = "green", "downregulation" = "red")) + # Set fill colors
  theme_classic() + 
  theme(panel.grid.minor.x = element_blank(),
      panel.grid.major.x = element_blank(),
      panel.grid.minor.y = element_blank(),
      panel.grid.major.y = element_blank()) +
      # axis.ticks.x = element_blank(),
      # axis.text.x = element_blank()) +
  ggtitle('Gene regulation in resistant celllines (as compared to corresponding sensitive cellline)')
Warning in geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated,  :
  Ignoring unknown aesthetics: width
Warning in geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated,  :
  Ignoring unknown aesthetics: width

  

ggsave("deseq/output/differential_genes_all_celllines.svg",
       plot = last_plot(),
       device = "svg",
       width = 10,
       height = 5,
       units = "in")

Close up of consistently upregulated genes

# End the plot between "categories" of genes, including less than 100 genes total
end_index = 1
for(i in 1:100) {
  if (plot_data$order[i] != plot_data$order[i+1]) {
    end_index = i
  }
}

plot_data_up = plot_data[1:end_index,]
plot_data_up$gene = rownames(plot_data_up)

print(plot_data_up)

ggplot(plot_data_up) +
  geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = 1), stat = "identity") +
  geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = 1), stat = "identity") +
  geom_text(aes(x = center_placement, y = 1, label = gene), size = 2, color = "black", angle = 90) +
  # geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = count), stat = "identity") +
  # geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = count), stat = "identity") +
  scale_y_continuous(limits = c(-max(plot_data_up$num_pairs_sig_down_regulated) -1, max(plot_data_up$num_pairs_sig_up_regulated)) +1) +
  # Set y-axis limits
  # coord_flip() +  # Flip the coordinates to create a sideways plot
  theme_minimal() +  # Apply a minimal theme
  labs(x = "Genes", y = "# resistant celllines showing significant regulation of gene") + # Set axis labels
  scale_fill_manual(values = c("upregulation" = "green", "downregulation" = "red")) + # Set fill colors
  theme_classic() + 
  theme(panel.grid.minor.x = element_blank(),
      panel.grid.major.x = element_blank(),
      panel.grid.minor.y = element_blank(),
      panel.grid.major.y = element_blank()) +
      # axis.ticks.x = element_blank(),
      # axis.text.x = element_blank()) +
  ggtitle('Zooming in on left hand side (extremely consistently upregulated genes)')
Warning in geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated,  :
  Ignoring unknown aesthetics: width
Warning in geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated,  :
  Ignoring unknown aesthetics: width
  # ggtitle('Genes extremely consistently upregulated in resistant celllines')

ggsave("deseq/output/consistently_upregulated_genes_all_celllines.svg",
       plot = last_plot(),
       device = "svg",
       width = 10,
       height = 5,
       units = "in")

Close up of consistently downregulated genes

# End the plot between "categories" of genes, including less than 100 genes total
start_index = 1
for(i in nrow(plot_data):(nrow(plot_data) - 99)) {
  if (plot_data$order[i] != plot_data$order[i-1]) {
    start_index = i
  }
}

plot_data_down = plot_data[start_index:nrow(plot_data),]
plot_data_down$center_placement = plot_data_down$center_placement - start_index + 1 # Start at 0
plot_data_down$gene = rownames(plot_data_down)

print(plot_data_down)

ggplot(plot_data_down) +
  geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = 1), stat = "identity") +
  geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = 1), stat = "identity") +
  geom_text(aes(x = center_placement, y = 2, label = gene), size = 2, color = "black", angle = 90) +
  # geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = count), stat = "identity") +
  # geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = count), stat = "identity") +
  scale_y_continuous(limits = c(-max(plot_data_down$num_pairs_sig_down_regulated) -1, max(plot_data_down$num_pairs_sig_up_regulated)) +1) +
  # Set y-axis limits
  # coord_flip() +  # Flip the coordinates to create a sideways plot
  theme_minimal() +  # Apply a minimal theme
  labs(x = "Genes", y = "# resistant celllines showing significant regulation of gene") + # Set axis labels
  scale_fill_manual(values = c("upregulation" = "green", "downregulation" = "red")) + # Set fill colors
  theme_classic() + 
  theme(panel.grid.minor.x = element_blank(),
      panel.grid.major.x = element_blank(),
      panel.grid.minor.y = element_blank(),
      panel.grid.major.y = element_blank()) +
      # axis.ticks.x = element_blank(),
      # axis.text.x = element_blank()) +
  ggtitle('Zooming in on right hand side (extremely consistently downregulated genes)')
Warning in geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated,  :
  Ignoring unknown aesthetics: width
Warning in geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated,  :
  Ignoring unknown aesthetics: width
  # ggtitle('Genes consistently upregulated in resistant celllines')

ggsave("deseq/output/consistently_downregulated_genes_all_celllines.svg",
       plot = last_plot(),
       device = "svg",
       width = 10,
       height = 5,
       units = "in")

Upset plots

Upset plots are akin to a Venn diagram and show the overlap of genes that were significantly up and down regulated by each resistant cellline (as compared to its respective control).

Note that these upset plots use the “distinct” mode (meaning that each represented intersection consists of the “intersection elements that belong to the sets defining the intersection but not to any other set”).

TODO: which of the hundreds of possible combinations to show? For now I’m separating upregulation and downregulation to decrease the number of possible intersections by a lot, but I’m still only showing the top 50 intersections (ranked by degree – i.e. number of cellLines that share that up or downregulated gene).

Combined up and down gene upset plot

combinedUpsetListInput <- vector(mode="list", length=length(sensitive_resistant_pairs))
for (pair in sensitive_resistant_pairs) {
  split <- strsplit(pair, "_vs_")
  cont <- split[[1]][2]
  exp <- split[[1]][1]
  for (dir in c("up", "down")) {
    regulatedGenesFile = str_interp("deseq/output/${exp}_vs_${cont}_significantly_${dir}regulated_genes.csv")
    regulatedGenes <- as.data.frame(read.csv(regulatedGenesFile, sep = ",", header = TRUE, row.names = 1))
    key = str_interp("${exp}_sig_${dir}reg_genes")
    combinedUpsetListInput[[key]] = rownames(regulatedGenes)
  }
}

upset(fromList(combinedUpsetListInput), nsets = 14, nintersects = 50, order.by = "degree")

Upregulated gene upset plot

Shows only the upregulated genes.

upregUpsetListInput <- vector(mode="list", length=length(sensitive_resistant_pairs))
for (pair in sensitive_resistant_pairs) {
  split <- strsplit(pair, "_vs_")
  cont <- split[[1]][2]
  exp <- split[[1]][1]
  
  upGenesFile <- str_interp("deseq/output/${exp}_vs_${cont}_significantly_upregulated_genes.csv")
  upGenes <- as.data.frame(read.csv(upGenesFile, sep = ",", header = TRUE, row.names = 1))
  key = str_interp("${exp}_sig_upreg_genes")
  upregUpsetListInput[[key]] = rownames(upGenes)
}

upset(fromList(upregUpsetListInput), nsets = 7, nintersects = 50, order.by = "degree")

Output the lists of genes corresponding to each column of the upset plot

fromList(upregUpsetListInput)
upregUpsetPlotGenes.df <- getUpsetPlotData(upregUpsetListInput)
write.csv(upregUpsetPlotGenes.df, file = "deseq/output/upset_plot_lists_significantly_upregulated_genes.csv")
upregUpsetPlotGenes.df

Downregulated gene upset plot

Shows only the downregulated genes.

downregUpsetListInput <- vector(mode="list", length=length(sensitive_resistant_pairs))

first_pair = TRUE
consistent_down_genes = c()

for (pair in sensitive_resistant_pairs) {
  split <- strsplit(pair, "_vs_")
  exp <- split[[1]][1]
  
  downGenesFile <- str_interp("deseq/output/${pair}_significantly_downregulated_genes.csv")
  downGenes <- as.data.frame(read.csv(downGenesFile, sep = ",", header = TRUE, row.names = 1))
  key = str_interp("${exp}_sig_downreg_genes")
  downregUpsetListInput[[key]] = rownames(downGenes)
  
  if (first_pair == TRUE) {
    consistent_down_genes = rownames(downGenes)
    first_pair = FALSE
  } else {
    consistent_down_genes = intersect(consistent_down_genes, rownames(downGenes))
  }
}

upset(fromList(downregUpsetListInput), nsets = 7, nintersects = 50, order.by = "degree")


print("Genes downregulated in all sensitive/resistant pairs")
[1] "Genes downregulated in all sensitive/resistant pairs"
print(consistent_down_genes)
[1] "NREP"      "TOX"       "NR2F1"     "LINC00960"

Output the lists of genes corresponding to each column of the upset plot

downregUpsetPlotGenes.df <- getUpsetPlotData(downregUpsetListInput)
write.csv(downregUpsetPlotGenes.df, file = "deseq/output/upset_plot_isolines_lists_significantly_downregulated_genes.csv")
downregUpsetPlotGenes.df

Upset plots across isolines

Gene included in isoline if it is sig upregulated in ANY of the cell lines

upsetListInput <- vector(mode="list", length=length(isolines))

for (isoline in unique(isolines$isoline)) {
  pairs = isolines[isolines$isoline == isoline, "pair"]
  upGenes <- list()
  
  for (pair in pairs) {
    upGenesFile = str_interp("deseq/output/${pair}_significantly_upregulated_genes.csv")
    newUpGenes <- as.data.frame(read.csv(upGenesFile, sep = ",", header = TRUE, row.names = 1))
    upGenes <- unique(c(upGenes, rownames(newUpGenes)))
  }
  
  upsetListInput[[isoline]] = upGenes
}

upset(fromList(upsetListInput), nsets = 7, nintersects = 50, order.by = "degree")


# Output the lists of genes corresponding to each column of the upset plot
upregUpsetPlotGenes.df <- getUpsetPlotData(upsetListInput)
write.csv(upregUpsetPlotGenes.df, file = "deseq/output/upset_plot_isolines_lists_significantly_upregulated_genes_any_sublines.csv")
upregUpsetPlotGenes.df

Gene included in isoline if it is sig upregulated in ALL of the cell lines

upsetListInput <- vector(mode="list", length=length(isolines))

for (isoline in unique(isolines$isoline)) {
  pairs = isolines[isolines$isoline == isoline, "pair"]
  
  pair = pairs[1]
  upGenesFile = str_interp("deseq/output/${pair}_significantly_upregulated_genes.csv")
  upGenes <- as.data.frame(read.csv(upGenesFile, sep = ",", header = TRUE, row.names = 1))
  upGenes <- rownames(upGenes)
  
  if (length(pairs) > 1) {
    for (pair in pairs[2:length(pairs)]) {
      upGenesFile = str_interp("deseq/output/${pair}_significantly_upregulated_genes.csv")
      newUpGenes <- as.data.frame(read.csv(upGenesFile, sep = ",", header = TRUE, row.names = 1))
      upGenes <- intersect(upGenes, rownames(newUpGenes))
    }
  }
  
  upsetListInput[[isoline]] = upGenes
}

upset(fromList(upsetListInput), nsets = 7, nintersects = 50, order.by = "degree")


# Output the lists of genes corresponding to each column of the upset plot
upregUpsetPlotGenes.df <- getUpsetPlotData(upsetListInput)
write.csv(upregUpsetPlotGenes.df, file = "deseq/output/upset_plot_isolines_lists_significantly_upregulated_genes_all_sublines.csv")
upregUpsetPlotGenes.df

Gene included in isoline if it is sig downregulated in ANY of the cell lines

upsetListInput <- vector(mode="list", length=length(isolines))

for (isoline in unique(isolines$isoline)) {
  pairs = isolines[isolines$isoline == isoline, "pair"]
  downGenes <- list()
  
  for (pair in pairs) {
    downGenesFile = str_interp("deseq/output/${pair}_significantly_downregulated_genes.csv")
    newDownGenes <- as.data.frame(read.csv(downGenesFile, sep = ",", header = TRUE, row.names = 1))
    downGenes <- unique(c(downGenes, rownames(newDownGenes)))
  }
  
  upsetListInput[[isoline]] = downGenes
}

upset(fromList(upsetListInput), nsets = 7, nintersects = 50, order.by = "degree")


# Output the lists of genes corresponding to each column of the upset plot
downregUpsetPlotGenes.df <- getUpsetPlotData(upsetListInput)
write.csv(downregUpsetPlotGenes.df, file = "deseq/output/upset_plot_isolines_lists_significantly_downregulated_genes_any_sublines.csv")
downregUpsetPlotGenes.df

Gene included in isoline if it is sig downregulated in ALL of the cell lines

upsetListInput <- vector(mode="list", length=length(isolines))

for (isoline in unique(isolines$isoline)) {
  pairs = isolines[isolines$isoline == isoline, "pair"]
  
  pair = pairs[1]
  downGenesFile = str_interp("deseq/output/${pair}_significantly_downregulated_genes.csv")
  downGenes <- as.data.frame(read.csv(downGenesFile, sep = ",", header = TRUE, row.names = 1))
  downGenes <- rownames(downGenes)
  
  if (length(pairs) > 1) {
    for (pair in pairs[2:length(pairs)]) {
      downGenesFile = str_interp("deseq/output/${pair}_significantly_downregulated_genes.csv")
      newDownGenes <- as.data.frame(read.csv(downGenesFile, sep = ",", header = TRUE, row.names = 1))
      downGenes <- intersect(downGenes, rownames(newDownGenes))
    }
  }
  
  upsetListInput[[isoline]] = downGenes
}

upset(fromList(upsetListInput), nsets = 7, nintersects = 50, order.by = "degree")


# Output the lists of genes corresponding to each column of the upset plot
downregUpsetPlotGenes.df <- getUpsetPlotData(upsetListInput)
write.csv(downregUpsetPlotGenes.df, file = "deseq/output/upset_plot_isolines_lists_significantly_downregulated_genes_all_sublines.csv")
upregUpsetPlotGenes.df

Upregulated pathway upset plot

Shows only the upregulated pathways.

upregUpsetListInput <- vector(mode="list", length=length(sensitive_resistant_pairs))
for (pair in sensitive_resistant_pairs) {
  split <- strsplit(pair, "_vs_")
  cont <- split[[1]][2]
  exp <- split[[1]][1]
  
  upPathwaysFile <- str_interp("deseq/output/${exp}_vs_${cont}_significantly_upregulated_pathways.csv")
  upPathways <- as.data.frame(read.csv(upPathwaysFile, sep = ",", header = TRUE, row.names = 1))
  key = str_interp("${exp}_sig_upreg_pathways")
  upregUpsetListInput[[key]] = upPathways$Description
}

upset(fromList(upregUpsetListInput), nsets = 7, nintersects = 50, order.by = "degree")

Output the lists of pathways corresponding to each column of the upset plot

fromList(upregUpsetListInput)
upregUpsetPlotPathways.df <- getUpsetPlotData(upregUpsetListInput)
write.csv(upregUpsetPlotPathways.df, file = "deseq/output/upset_plot_lists_significantly_upregulated_pathways.csv")
upregUpsetPlotPathways.df

Downregulated pathway upset plot

Shows only the downregulated pathways.

downregUpsetListInput <- vector(mode="list", length=length(sensitive_resistant_pairs))
for (pair in sensitive_resistant_pairs) {
  split <- strsplit(pair, "_vs_")
  cont <- split[[1]][2]
  exp <- split[[1]][1]
  
  downPathwaysFile <- str_interp("deseq/output/${exp}_vs_${cont}_significantly_downregulated_pathways.csv")
  downPathways <- as.data.frame(read.csv(downPathwaysFile, sep = ",", header = TRUE, row.names = 1))
  key = str_interp("${exp}_sig_downreg_pathways")
  downregUpsetListInput[[key]] = downPathways$Description
}

upset(fromList(downregUpsetListInput), nsets = 7, nintersects = 50, order.by = "degree")

Output the lists of pathways corresponding to each column of the upset plot

fromList(downregUpsetListInput)
downregUpsetPlotPathways.df <- getUpsetPlotData(downregUpsetListInput)
write.csv(downregUpsetPlotPathways.df, file = "deseq/output/upset_plot_lists_significantly_downregulated_pathways.csv")
downregUpsetPlotPathways.df

Read in platinum mechanism data

platinumGeneData <- as.data.frame(read.table("deseq/specific-pathways/platinum-list-all-mechanisms.txt", sep = "\n", header = TRUE))
row.names(platinumGeneData) <- platinumGeneData[,1]

platinumGeneData

Visualizing differential genes across sensitive/resistant pairs

Shows gene expression across sensitive/resistant pairs. Includes genes that are consistently regulated in at least 3 sensitive/resistant pairs. Uses a mean-centered approach.

consistentlyRegulatedGenes = rownames(full_results_genes[full_results_genes$num_pairs_sig_regulated > 2, ])

dds.all <- DESeqDataSetFromMatrix(countData = countmatrix.all, colData = metadata.all, design = ~ CellLine + Replicate)
converting counts to integer mode
Warning in DESeqDataSet(se, design = design, ignoreRank) :
  61 duplicate rownames were renamed by adding numbers
Warning in DESeqDataSet(se, design = design, ignoreRank) :
  some variables in design formula are characters, converting to factors
  the design formula contains one or more numeric variables with integer values,
  specifying a model with increasing fold change for higher values.
  did you mean for this to be a factor? if so, first convert
  this variable to a factor using the factor() function
annotation_col <- as.data.frame(colData(dds.all)[,c("Resistant", "IsoLine")])
annotation_col$Resistant <- as.factor(annotation_col$Resistant)
annotation_col$IsoLine <- as.factor(annotation_col$IsoLine)
annotation_col <- as.data.frame(annotation_col)

select <- TPM.log[rownames(TPM.log) %in% consistentlyRegulatedGenes,]

mat <- as.matrix(select)
mat <- mat - rowMeans(mat)
pheatmap(mat,
  cluster_rows = TRUE,
  cluster_cols = FALSE,
  show_rownames = FALSE,
  labels_col = colnames(select),
  annotation_col = annotation_col,
  legend = TRUE,
  cellwidth = 5,
  fontsize = 5,
  fontsize_row = 2,
  border_color=NA
)

LS0tCnRpdGxlOiAiU2Vjb25kYXJ5IGFuYWx5c2lzIG9mIDIzMDMyNC0zd2F5LW1lcmdlIFJOQS1zZXEgc2Vuc2l0aXZlIHZzIHJlc2lzdGFudCBjZWxsIGxpbmVzIGluIGlzb2xpbmVzIFBFTywgUEVBLCBPVkNBUjMsIE9WQ0FSNCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgSW50cm9kdWN0aW9uICAKCkdvYWw6IHRvIGNvbXBhcmUgdGhlIGRpZmZlcmVuY2UgaW4gZ2VuZSBleHByZXNzaW9uIGJldHdlZW4gc2Vuc2l0aXZlIGFuZCByZXNpc3RhbnQgY2VsbCBsaW5lcyBvZiB0aHJlZSBkaWZmZXJlbnQgbGluZXMgb2YgdHVtb3JzLgoKUk5BLXNlcSB3YXMgcnVuIGZvciB0aHJlZSBpc29nZW5pYyB0dW1vciBjZWxsIGxpbmVzIChQRU8xLCBQRU80LCBhbmQgUEVPNikKU2FtcGxlIHByZXBhcmF0aW9uIHdhcyBwZXJmb3JtZWQgaW4gRHIuIExhbmcncyBsYWIuIFByZXBhcmF0aW9uIG9mIGNlbGxzIGFuZCBSTkEgZXh0cmFjdGlvbiB3YXMgZG9uZSBieSBLZW5kcmEsIEpvc2llLCBhbmQgU3lkbmV5LgpSTkEgc2VxIGxpYnJhcnkgcHJlcCB3YXMgZG9uZSBieSBLcmlzdGVuLgpBbmFseXNpcyBkb25lIGJ5IFJ5YW4uCgpCaW9sb2dpY2FsIFF1ZXN0aW9uczoKCiAqIEhvdyBkb2VzIHRoZSBnZW5lIGV4cHJlc3Npb24gZGlmZmVyIGJldHdlZW4gdGhlc2UgdGhyZWUgY2VsbCBsaW5lcz8KICogSG93IGRvZXMgdGhlIGdlbmUgZXhwcmVzc2lvbiBkaWZmZXIgYmV0d2VlbiBzZW5zaXRpdmUgYW5kIHJlc2lzdGFudCBjZWxsIGxpbmVzCgojIyBJbnB1dHMKCklucHV0cyBjb25zaXN0ZWQgb2Y6ICAKCiAqIE1ldGFkYXRhIHNwcmVhZHNoZWV0IGZvciBERVNlcTIKICogc2FsbW9uLm1lcmdlZC5nZW5lX2NvdW50cy50c3YgZmlsZSBmcm9tIG5mLWNvcmUvcm5hc2VxIHBpcGVsaW5lIG91dHB1dCAgCiAqIHNhbG1vbi5tZXJnZWQuZ2VuZV90cG0udHN2CgpgYGB7ciBsb2FkIHBhY2thZ2VzLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KHZzbikKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShiaW9tYVJ0KQpsaWJyYXJ5KERFU2VxQW5hbHlzaXMpCmxpYnJhcnkoVXBTZXRSKQpsaWJyYXJ5KGdwcm9maWxlcjIpCmxpYnJhcnkocnJ2Z28pCmxpYnJhcnkoR08uZGIpCmxpYnJhcnkoZ2dmb3J0aWZ5KQpHTyA8LSBhcy5saXN0KEdPVEVSTSkKYGBgCgpEZWZpbmUgZnVuY3Rpb25zIHRvIG1ha2UgYW4gaW50ZXJhY3RpdmUgcGxvdHMgZnJvbSByZXZpZ28gZGF0YSAoY29kZSBiYXNlZCBvZmYgYHNoaW55X3JydmdvYCkKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CnJldmlnb19zY2F0dGVycGxvdCA8LSBmdW5jdGlvbihzaW1NYXRyaXgsIHJlZHVjZWRUZXJtcykgewogIHggPC0gY21kc2NhbGUoYXMubWF0cml4KGFzLmRpc3QoMS1zaW1NYXRyaXgpKSwgZWlnPVRSVUUsIGs9MikKICBkZiA8LSBjYmluZChhcy5kYXRhLmZyYW1lKHgkcG9pbnRzKSwKICAgICAgICAgICAgICByZWR1Y2VkVGVybXNbbWF0Y2gocm93bmFtZXMoeCRwb2ludHMpLCByZWR1Y2VkVGVybXMkZ28pLCBjKCJ0ZXJtIiwgInBhcmVudCIsICJwYXJlbnRUZXJtIiwgInNpemUiKV0pCiAgCiAgcCA8LSAgc2NhdHRlclBsb3Qoc2ltTWF0cml4LCByZWR1Y2VkVGVybXMsIGFkZExhYmVsPUZBTFNFKQogIAogIHAgPC0gcCArIGdlb21fdGV4dChhZXMobGFiZWw9cGFyZW50VGVybSksIGRhdGE9c3Vic2V0KGRmLCBwYXJlbnQgPT0gcm93bmFtZXMoZGYpKSwgc2l6ZT0zKSAjIHNjYXR0ZXIgbGFiZWxzCiAgcCA8LSBwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdub25lJykgIyBzY2F0dGVyIGxlZ2VuZCAKICBnZ3Bsb3RseShwKQp9CgpyZXZpZ29faGVhdG1hcCA8LSBmdW5jdGlvbihzaW1NYXRyaXgsIHJlZHVjZWRUZXJtcykgewogIGFubiA8LSByZWR1Y2VkVGVybXMkdGVybVttYXRjaChyZWR1Y2VkVGVybXMkcGFyZW50LCByZWR1Y2VkVGVybXMkZ28pXQogIGFubiA8LSBkYXRhLmZyYW1lKGFublttYXRjaChyb3duYW1lcyhzaW1NYXRyaXgpLCByZWR1Y2VkVGVybXMkZ28pXSkKICBjb2xuYW1lcyhhbm4pIDwtICIiCiAgaGVhdG1hcGx5OjpoZWF0bWFwbHkoc2ltTWF0cml4LCByb3dfc2lkZV9jb2xvcnM9YW5uLCBwbG90X21ldGhvZD0icGxvdGx5IiwKICAgICAgICAgICAgICAgICAgICAgICBzeW1tPVRSVUUsIGxhYlJvdz1OVUxMLCBrZXkudGl0bGU9IlNpbWlsYXJpdHkiLAogICAgICAgICAgICAgICAgICAgICAgIHNob3d0aWNrbGFiZWxzPWMoRkFMU0UsIFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgIHdpZHRoPTEwMjQsIGhlaWdodD0xMDI0LAogICAgICAgICAgICAgICAgICAgICAgIHJvd19zaWRlX3BhbGV0dGU9cnJ2Z286OjpnZ19jb2xvcl9odWUsCiAgICAgICAgICAgICAgICAgICAgICAgc2hvd19kZW5kcm9ncmFtPXJlcChUUlVFLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICBmb250c2l6ZV9yb3c9NikgJT4lCiAgY29sb3JiYXIoeGFuY2hvcj0ibGVmdCIsIHlhbmNob3I9ImJvdHRvbSIsIGxlbj0uMiwgdGlja2ZvbnQ9bGlzdChzaXplPTYpLCB3aGljaD0xKSAlPiUKICBjb2xvcmJhcih4YW5jaG9yPSJsZWZ0IiwgeWFuY2hvcj0iYm90dG9tIiwgbGVuPS41LCB0aWNrZm9udD1saXN0KHNpemU9NiksIHdoaWNoPTIpICU+JQogIGxheW91dCh3aWR0aD0xMDAwKQp9CmBgYAoKRGVmaW5lIHVwc2V0IHBsb3QgaGVscGVyIGZ1bmN0aW9ucyAoY29kZSBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS9obXMtZGJtaS9VcFNldFIvaXNzdWVzLzg1I2lzc3VlY29tbWVudC00MTU0ODA5NTQpCmBgYHtyIGluY2x1ZGU9RkFMU0V9Cm92ZXJsYXBHcm91cHMgPC0gZnVuY3Rpb24gKGxpc3RJbnB1dCwgc29ydCA9IFRSVUUpIHsKICAjIGxpc3RJbnB1dCBjb3VsZCBsb29rIGxpa2UgdGhpczoKICAjICRvbmUKICAjIFsxXSAiYSIgImIiICJjIiAiZSIgImciICJoIiAiayIgImwiICJtIgogICMgJHR3bwogICMgWzFdICJhIiAiYiIgImQiICJlIiAiaiIKICAjICR0aHJlZQogICMgWzFdICJhIiAiZSIgImYiICJnIiAiaCIgImkiICJqIiAibCIgIm0iCiAgbGlzdElucHV0bWF0ICAgIDwtIGZyb21MaXN0KGxpc3RJbnB1dCkgPT0gMQogICMgICAgIG9uZSAgIHR3byB0aHJlZQogICMgYSAgVFJVRSAgVFJVRSAgVFJVRQogICMgYiAgVFJVRSAgVFJVRSBGQUxTRQogICMuLi4KICAjIGNvbmRlbnNpbmcgbWF0cml4IHRvIHVuaXF1ZSBjb21iaW5hdGlvbnMgZWxlbWVudHMKICBsaXN0SW5wdXR1bmlxdWUgPC0gdW5pcXVlKGxpc3RJbnB1dG1hdCkKICBncm91cGxpc3QgPC0gbGlzdCgpCiAgIyBnb2luZyB0aHJvdWdoIGFsbCB1bmlxdWUgY29tYmluYXRpb25zIGFuZCBjb2xsZWN0IGVsZW1lbnRzIGZvciBlYWNoIGluIGEgbGlzdAogIGZvciAoaSBpbiAxOm5yb3cobGlzdElucHV0dW5pcXVlKSkgewogICAgY3VycmVudFJvdyA8LSBsaXN0SW5wdXR1bmlxdWVbaSxdCiAgICBteWVsZW1lbnRzIDwtIHdoaWNoKGFwcGx5KGxpc3RJbnB1dG1hdCwxLGZ1bmN0aW9uKHgpIGFsbCh4ID09IGN1cnJlbnRSb3cpKSkKICAgIGF0dHIobXllbGVtZW50cywgImdyb3VwcyIpIDwtIGN1cnJlbnRSb3cKICAgIGdyb3VwbGlzdFtbcGFzdGUoY29sbmFtZXMobGlzdElucHV0dW5pcXVlKVtjdXJyZW50Um93XSwgY29sbGFwc2UgPSAiOiIpXV0gPC0gbXllbGVtZW50cwogICAgbXllbGVtZW50cwogICAgIyBhdHRyKCwiZ3JvdXBzIikKICAgICMgICBvbmUgICB0d28gdGhyZWUgCiAgICAjIEZBTFNFIEZBTFNFICBUUlVFIAogICAgIyAgZiAgaSAKICAgICMgMTIgMTMgCiAgfQogIGlmIChzb3J0KSB7CiAgICBncm91cGxpc3QgPC0gZ3JvdXBsaXN0W29yZGVyKHNhcHBseShncm91cGxpc3QsIGZ1bmN0aW9uKHgpIGxlbmd0aCh4KSksIGRlY3JlYXNpbmcgPSBUUlVFKV0KICB9CiAgYXR0cihncm91cGxpc3QsICJlbGVtZW50cyIpIDwtIHVuaXF1ZSh1bmxpc3QobGlzdElucHV0KSkKICByZXR1cm4oZ3JvdXBsaXN0KQp9CgpnZXRVcHNldFBsb3REYXRhIDwtIGZ1bmN0aW9uKGxpc3RJbnB1dCkgewogICMgbGlzdElucHV0IGNvdWxkIGxvb2sgbGlrZSB0aGlzOgogICMgJG9uZQogICMgWzFdICJhIiAiYiIgImMiICJlIiAiZyIgImgiICJrIiAibCIgIm0iCiAgIyAkdHdvCiAgIyBbMV0gImEiICJiIiAiZCIgImUiICJqIgogICMgJHRocmVlCiAgIyBbMV0gImEiICJlIiAiZiIgImciICJoIiAiaSIgImoiICJsIiAibSIKICB1cHNldFBsb3REYXRhLm1lc3N5IDwtIG92ZXJsYXBHcm91cHMobGlzdElucHV0KQogIHVwc2V0UGxvdERhdGEgPC0gcHVycnI6Om1hcCh1cHNldFBsb3REYXRhLm1lc3N5LCB+IGF0dHIodXBzZXRQbG90RGF0YS5tZXNzeSwgImVsZW1lbnRzIilbLnhdKQogICMgQWRkcyBOQSB2YWx1ZXMgdW50aWwgZWFjaCBpcyB0aGUgc2FtZSBsZW5ndGggc28gd2UgY2FuIGZvcm1hdCBpbiBhIGRhdGEgZnJhbWUKICB1cHNldFBsb3REYXRhLmRmIDwtIGFzLmRhdGEuZnJhbWUobGFwcGx5KHVwc2V0UGxvdERhdGEsIGBsZW5ndGg8LWAsIG1heChsZW5ndGhzKHVwc2V0UGxvdERhdGEpKSkpCiAgcmV0dXJuKHVwc2V0UGxvdERhdGEuZGYpCn0KYGBgCgojIyBERVNlcTIgLSBzZXR1cAoKIyMjIFJlYWQgaW4gbWV0YWRhdGEgdGFibGUKCmBgYHtyfQpzZW5zaXRpdmVfcmVzaXN0YW50X3BhaXJzIDwtIGMoIk9WQ0FSM0FfdnNfT1ZDQVIzIiwgIk9WQ0FSM0JfdnNfT1ZDQVIzIiwgIk9WQ0FSNEFfdnNfT1ZDQVI0IiwgIk9WQ0FSNEJfdnNfT1ZDQVI0IiwgIlBFQTJfdnNfUEVBMSIsICJQRU82X3ZzX1BFTzEiLCAiUEVPNF92c19QRU8xIikKCmlzb2xpbmVzIDwtIGRhdGEuZnJhbWUocGFpciA9IHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMsCiAgICAgICAgICAgICAgICAgICAgICAgaXNvbGluZSA9IGMoIk9WQ0FSMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9WQ0FSMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9WQ0FSNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9WQ0FSNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBFQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBFTyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBFTyIpKQpgYGAKCmBgYHtyIGxvYWQgbWV0YXRhYmxlfQptZXRhZGF0YS5hbGwgPC0gYXMuZGF0YS5mcmFtZShyZWFkLnRhYmxlKCJkZXNlcS9tZXRhZGF0YS5jc3YiLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUpKQpyb3duYW1lcyhtZXRhZGF0YS5hbGwpIDwtIG1ldGFkYXRhLmFsbCRTaG9ydE5hbWUKCiMgU2hvdWxkIHB1dCB0aGlzIGluIHRoZSBtZXRhZGF0YSBmaWxlLCBidXQganVzdCBkb2luZyB0aGlzIHRvIHNhdmUgdGltZQpmb3IgKHJvdyBpbiAxOm5yb3cobWV0YWRhdGEuYWxsKSkgewogICAgaXNvZ2VuaWNSYW5rIDwtIDEKICAgIHJlc2lzdGFudCA8LSAwCiAgICBpZiAobWV0YWRhdGEuYWxsJENlbGxMaW5lW3Jvd10gJWluJSBsaXN0KCJPVkNBUjNBIiwgIk9WQ0FSNEEiLCAiUEVBMiIsICJQRU80IikpIHsKICAgICAgaXNvZ2VuaWNSYW5rIDwtIDIKICAgICAgcmVzaXN0YW50IDwtIDEKICAgIH0gZWxzZSBpZiAobWV0YWRhdGEuYWxsJENlbGxMaW5lW3Jvd10gJWluJSBsaXN0KCJPVkNBUjNCIiwgIk9WQ0FSNEIiLCAiUEVPNiIpKSB7CiAgICAgIGlzb2dlbmljUmFuayA8LSAzCiAgICAgIHJlc2lzdGFudCA8LSAxCiAgICB9CiAgICBtZXRhZGF0YS5hbGwkSXNvZ2VuaWNSYW5rW3Jvd10gPC0gaXNvZ2VuaWNSYW5rCiAgICBtZXRhZGF0YS5hbGwkUmVzaXN0YW50W3Jvd10gPC0gcmVzaXN0YW50Cn0KbWV0YWRhdGEuYWxsCmBgYAoKIyMjIExvYWQgY291bnQgbWF0cml4CgpgYGB7ciByZWFkIGNvdW50IG1hdHJpeH0KY291bnRtYXRyaXggPC0gYXMubWF0cml4KHJlYWQudGFibGUoIi4uL3N0YXJfc2FsbW9uL3NhbG1vbi5tZXJnZWQuZ2VuZV9jb3VudHMudHN2Iiwgc2VwID0gIlx0IiwgaGVhZGVyID0gVFJVRSkpCnJvdy5uYW1lcyhjb3VudG1hdHJpeCkgPC0gY291bnRtYXRyaXhbLCAiZ2VuZV9uYW1lIl0KY291bnRtYXRyaXggPC0gY291bnRtYXRyaXhbLCAzOm5jb2woY291bnRtYXRyaXgpXQpjb3VudG1hdHJpeC5hbGwgPC0gbWF0cml4KGFzLm51bWVyaWMoY291bnRtYXRyaXgpLCBuY29sID0gbmNvbChjb3VudG1hdHJpeCksIGRpbW5hbWVzID0gbGlzdChyb3duYW1lcyhjb3VudG1hdHJpeCksIGNvbG5hbWVzKGNvdW50bWF0cml4KSkpCmNvdW50bWF0cml4LmFsbCA8LSByb3VuZChjb3VudG1hdHJpeC5hbGwpCmNvdW50bWF0cml4LmFsbCA8LSBjb3VudG1hdHJpeC5hbGxbLCBtZXRhZGF0YS5hbGwkTG9uZ05hbWVdCmNvbG5hbWVzKGNvdW50bWF0cml4LmFsbCkgPC0gbWV0YWRhdGEuYWxsJFNob3J0TmFtZVttYXRjaChjb2xuYW1lcyhjb3VudG1hdHJpeC5hbGwpLCBtZXRhZGF0YS5hbGwkTG9uZ05hbWUpXSAjIFJlbmFtZXMgdGhlIFNhbG1vbiBjb3VudG1hdHJpeCB1c2luZyBmb3JtYXR0ZWQgc2hvcnQgbmFtZQphcy5kYXRhLmZyYW1lKGNvdW50bWF0cml4LmFsbCkKYGBgCgojIyMgTG9hZCBUUE0gbWF0cml4CmBgYHtyfQpUUE0gPC0gYXMubWF0cml4KHJlYWQuZGVsaW0oIi4uL3N0YXJfc2FsbW9uL3NhbG1vbi5tZXJnZWQuZ2VuZV90cG0udHN2Iiwgc2VwPSJcdCIsIHJvdy5uYW1lcz0iZ2VuZV9pZCIpKQpUUE0gPC0gVFBNWywtMV0KVFBNIDwtIG1hdHJpeChhcy5udW1lcmljKFRQTSksIG5jb2wgPSBuY29sKFRQTSksIGRpbW5hbWVzID0gbGlzdChyb3duYW1lcyhUUE0pLCBjb2xuYW1lcyhUUE0pKSkKVFBNLmxvZyA8LSBsb2coVFBNICsgMSkKYXMuZGF0YS5mcmFtZShUUE0ubG9nKQpjb2xuYW1lcyhUUE0ubG9nKSA8LSBtZXRhZGF0YS5hbGwkU2hvcnROYW1lCmBgYAoKIyMgUENBIHBsb3QKClJ1bm5pbmcgREVTZXEgb24gYWxsIG9mIHRoZSBjZWxsbGluZXMgdG9nZXRoZXIgdG8gZ2V0IG5vcm1hbGl6ZWQgZGF0YS4KCmBgYHtyfQpkZHMuYWxsID0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeCgKICBjb3VudERhdGEgPSBjb3VudG1hdHJpeC5hbGwsCiAgY29sRGF0YSA9IG1ldGFkYXRhLmFsbCwKICBkZXNpZ24gPSB+IFJlcGxpY2F0ZSArIENlbGxMaW5lCikKZGRzLmFsbCA9IERFU2VxKGRkcy5hbGwpCnNhdmUoZGRzLmFsbCwgZmlsZSA9IHN0cl9pbnRlcnAoImRlc2VxL1JkYXRhL2FsbF9jZWxsbGluZXNfZGRzLlJEYXRhIikpCiMgbG9hZChzdHJfaW50ZXJwKCJkZXNlcS9SZGF0YS9hbGxfY2VsbGxpbmVzX2Rkcy5SRGF0YSIpKQpgYGAKVHJhbnNmb3JtIGRhdGEgd2l0aCBhIHZhcmlhbmNlIHN0YWJpbGl6ZWQgdHJhbnNmb3JtYXRpb24KCmBgYHtyfQp2c2QuYWxsID0gYXNzYXkodnN0KGRkcy5hbGwpKQptZWFuU2RQbG90KHZzZC5hbGwpCmBgYAoKPCEtLSBQQ0EgUGxvdCAtLT4KCjwhLS0gT2xkIGZvcm1hdHRpbmcgLS0+Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSB2c2QuYWxsID0gYXMuZGF0YS5mcmFtZSh2c2QuYWxsKSAtLT4KPCEtLSB0cmFuc2Zvcm1lZCA9IGFzLmRhdGEuZnJhbWUodCh2c2QuYWxsKSkgLS0+CjwhLS0gcGNhUmVzID0gcHJjb21wKHRyYW5zZm9ybWVkWywtMV0sIHNjYWxlID0gVFJVRSkgLS0+Cgo8IS0tIHBjYV9tZXRhZGF0YSA9IGNvbERhdGEoZGRzLmFsbClbLGMoIkNlbGxMaW5lIiwgIlJlc2lzdGFudCIsICJJc29MaW5lIildIC0tPgo8IS0tIHBjYV9tZXRhZGF0YSRSZXNpc3RhbmNlIDwtIGlmZWxzZShwY2FfbWV0YWRhdGEkUmVzaXN0YW50ID09IDAsICJTZW5zaXRpdmUiLCAiUmVzaXN0YW50IikgLS0+CjwhLS0gcm93Lm5hbWVzKHBjYV9tZXRhZGF0YSkgPC0gY29sRGF0YShkZHMuYWxsKVssYygiU2hvcnROYW1lIildIC0tPgoKPCEtLSB0cmFuc2Zvcm1lZCRJc29MaW5lIDwtIGZhY3RvcihwY2FfbWV0YWRhdGEkSXNvTGluZSkgLS0+CjwhLS0gdHJhbnNmb3JtZWQkQ2VsbExpbmUgPC0gZmFjdG9yKHBjYV9tZXRhZGF0YSRDZWxsTGluZSkgLS0+CjwhLS0gdHJhbnNmb3JtZWQkUmVzaXN0YW5jZSA8LSBmYWN0b3IocGNhX21ldGFkYXRhJFJlc2lzdGFuY2UpIC0tPgoKPCEtLSBhdXRvcGxvdChwY2FSZXMsIGRhdGEgPSB0cmFuc2Zvcm1lZCwgY29sb3VyID0gIkNlbGxMaW5lIiwgc2hhcGUgPSAiUmVzaXN0YW5jZSIsIHNpemUgPSAzKSAtLT4KPCEtLSAjIGdnc2F2ZSgiZGVzZXEvb3V0cHV0L3BjYV9hbGxfY2VsbGxpbmVzLnN2ZyIsIC0tPgo8IS0tICMgICAgICAgIHBsb3QgPSBsYXN0X3Bsb3QoKSwgLS0+CjwhLS0gIyAgICAgICAgZGV2aWNlID0gInN2ZyIsIC0tPgo8IS0tICMgICAgICAgIHdpZHRoID0gNiwgLS0+CjwhLS0gIyAgICAgICAgdW5pdHMgPSAiaW4iKSAtLT4KCjwhLS0gIyBDb21wdXRlIHRvcCBjb250cmlidXRpbmcgZ2VuZXMgdG8gUEMxIGFuZCBQQzIgLS0+CjwhLS0gbG9hZGluZ3NfcGMxIDwtIHBjYVJlcyRyb3RhdGlvblssIlBDMSJdIC0tPgo8IS0tIGNvbnRyaWJ1dGluZ19nZW5lc19wYzEgPC0gbG9hZGluZ3NfcGMxW29yZGVyKGFicyhsb2FkaW5nc19wYzEpLCBkZWNyZWFzaW5nID0gVFJVRSldIC0tPgo8IS0tIHByaW50KGFzLmRhdGEuZnJhbWUoY29udHJpYnV0aW5nX2dlbmVzX3BjMSkpIC0tPgoKPCEtLSBsb2FkaW5nc19wYzIgPC0gcGNhUmVzJHJvdGF0aW9uWywgIlBDMiJdIC0tPgo8IS0tIGNvbnRyaWJ1dGluZ19nZW5lc19wYzIgPC0gbG9hZGluZ3NfcGMyW29yZGVyKGFicyhsb2FkaW5nc19wYzIpLCBkZWNyZWFzaW5nID0gVFJVRSldIC0tPgo8IS0tIHByaW50KGFzLmRhdGEuZnJhbWUoY29udHJpYnV0aW5nX2dlbmVzX3BjMikpIC0tPgoKPCEtLSBgYGAgLS0+CgpQQ0EgcGxvdCBmb3JtYXR0ZWQgZm9yIHBhcGVyCgpgYGB7cn0KdnNkLmFsbC5mdWxsb2JqID0gdnN0KGRkcy5hbGwpCnBjYURhdGEgPC0gcGxvdFBDQSh2c2QuYWxsLmZ1bGxvYmosIGludGdyb3VwPWMoIkNlbGxMaW5lIiwgIlJlc2lzdGFudCIpLCByZXR1cm5EYXRhPVRSVUUpCnBjYURhdGEkUmVzaXN0YW5jZSA8LSBpZmVsc2UocGNhRGF0YSRSZXNpc3RhbnQgPT0gMCwgIlNlbnNpdGl2ZSIsICJSZXNpc3RhbnQiKQpwZXJjZW50VmFyIDwtIHJvdW5kKDEwMCAqIGF0dHIocGNhRGF0YSwgInBlcmNlbnRWYXIiKSkKbXlDb2xvcnMgPC0gYygiIzc2QUI3RSIsICIjNjNFNjc4IiwgIiMwRTlEMUYiLCAiIzdCODdGRCIsICIjMUQzMkZCIiwgIiMxNDUyRkIiLCAiI0U4NzQyNiIsICIjREFDNDI2IiwgIiM4NzY5RTciLCAiI0UwMTlFNyIsICIjOUIxOUU3IikKbmFtZXMobXlDb2xvcnMpIDwtIGxldmVscyhwY2FEYXRhJENlbGxMaW5lKQpjb2xTY2FsZSA8LSBzY2FsZV9jb2xvdXJfbWFudWFsKG5hbWUgPSAiQ2VsbExpbmUiLHZhbHVlcyA9IG15Q29sb3JzKQpwY2EgPC0gZ2dwbG90KHBjYURhdGEsIGFlcyhQQzEsIFBDMiwgY29sb3I9Q2VsbExpbmUsIHNoYXBlPVJlc2lzdGFuY2UsIGxhYmVsPSIiKSkgKwogIGdlb21fcG9pbnQoc2l6ZT0zKSArCiAgZ2VvbV90ZXh0KGhqdXN0PTAsIHZqdXN0PTApICsKICB4bGFiKHBhc3RlMCgiUEMxOiAiLHBlcmNlbnRWYXJbMV0sIiUgdmFyaWFuY2UiKSkgKwogIHlsYWIocGFzdGUwKCJQQzI6ICIscGVyY2VudFZhclsyXSwiJSB2YXJpYW5jZSIpKSArCiAgY29vcmRfZml4ZWQoKSArCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgY29sU2NhbGUKcGNhCmdnc2F2ZSgiZGVzZXEvb3V0cHV0L3BjYV9hbGxfY2VsbGxpbmVzX2Zvcm1hdHRlZC5zdmciLAogICAgICAgcGxvdCA9IGxhc3RfcGxvdCgpLAogICAgICAgZGV2aWNlID0gInN2ZyIsCiAgICAgICB3aWR0aCA9IDYsCiAgICAgICB1bml0cyA9ICJpbiIpCmBgYAoKIyMgREVTZXEyIC0gQW5hbHlzaXMgb2YgZWFjaCBTZW5zaXRpdmUvUmVzaXN0YW50IHBhaXIKCmBgYHtyIHNlbnNpdGl2ZSByZXNpc3RhbnQgcGFpciwgcmVzdWx0cyA9IEZBTFNFfQpmb3IgKHBhaXIgaW4gc2Vuc2l0aXZlX3Jlc2lzdGFudF9wYWlycykgewogIHNwbGl0IDwtIHN0cnNwbGl0KHBhaXIsICJfdnNfIikKICBjb250IDwtIHNwbGl0W1sxXV1bMl0KICBleHAgPC0gc3BsaXRbWzFdXVsxXQogIAogICMgQ3JlYXRlcyBhbm90aGVyIG5vdGVib29rIHRoYXQgc2hhcmVzIHRoZSBzYW1lIGVudmlyb25tZW50CiAgb3V0RmlsZSA8LSBzdHJfaW50ZXJwKCJnZW5lcmF0ZWQtbm90ZWJvb2tzL2Rlc2VxLWFuYWx5c2lzLSR7cGFpcn0uaHRtbCIpCiAgcHJpbnQoc3RyX2ludGVycCgiQ3JlYXRpbmcgbm90ZWJvb2sgZm9yICR7ZXhwfSB2cyAke2NvbnR9IikpCiAgcm1hcmtkb3duOjpyZW5kZXIoJ2Rlc2VxL3NpbmdsZS1jZWxsbGluZS12cy1jb250cm9sLlJtZCcsIAogICAgICAgICAgICAgICAgICAgIG91dHB1dF9maWxlID0gb3V0RmlsZSwgCiAgICAgICAgICAgICAgICAgICAgcGFyYW1zID0gbGlzdChjb250cm9sQ2VsbExpbmUgPSBjb250LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGVyaW1lbnRhbENlbGxMaW5lID0gZXhwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY291bnRtYXRyaXguYWxsID0gY291bnRtYXRyaXguYWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEuYWxsID0gbWV0YWRhdGEuYWxsKSkKfQpgYGAKCiMjIyBDb21iaW5lIHJlc3VsdHMgYWNyb3NzIHBhaXJzCgpDb21iaW5lIGRpZmZlcmVudGlhbCBnZW5lIGRhdGEKCmBgYHtyIGNvbWJpbmUgZGVzZXEgcmVzdWx0c30KZnVsbF9yZXN1bHRzX2dlbmVzIDwtIGRhdGEuZnJhbWUoKQpmb3IgKHBhaXIgaW4gc2Vuc2l0aXZlX3Jlc2lzdGFudF9wYWlycykgewogIHBhaXJfcmVzdWx0c19maWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X2Rlc2VxX3Jlc3VsdHMuY3N2IikKICBwYWlyX3Jlc3VsdHMgPC0gYXMuZGF0YS5mcmFtZShyZWFkLmNzdihwYWlyX3Jlc3VsdHNfZmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICBwYWlyX3Jlc3VsdHNfZm9ybWF0dGVkIDwtIHBhaXJfcmVzdWx0c1ssIGMoInBhZGoiLCAibG9nMkZvbGRDaGFuZ2UiKV0KICBjb2xuYW1lcyhwYWlyX3Jlc3VsdHNfZm9ybWF0dGVkKSA8LSBjKHN0cl9pbnRlcnAoIiR7cGFpcn1fcGFkaiIpLCBzdHJfaW50ZXJwKCIke3BhaXJ9X2wyZmMiKSkKICBwYWlyX3Jlc3VsdHNfZm9ybWF0dGVkJGdlbmUgPC0gcm93bmFtZXMocGFpcl9yZXN1bHRzX2Zvcm1hdHRlZCkKCiAgaWYgKG5jb2woZnVsbF9yZXN1bHRzX2dlbmVzKSA9PSAwKSB7CiAgICBmdWxsX3Jlc3VsdHNfZ2VuZXMgPSBwYWlyX3Jlc3VsdHNfZm9ybWF0dGVkCiAgfSBlbHNlIHsKICAgIGZ1bGxfcmVzdWx0c19nZW5lcyA8LSBtZXJnZShmdWxsX3Jlc3VsdHNfZ2VuZXMsIHBhaXJfcmVzdWx0c19mb3JtYXR0ZWQsIGJ5ID0gImdlbmUiLCBhbGwgPSBUUlVFKQogIH0KfQoKcm93bmFtZXMoZnVsbF9yZXN1bHRzX2dlbmVzKSA8LSBmdWxsX3Jlc3VsdHNfZ2VuZXMkZ2VuZQpmdWxsX3Jlc3VsdHNfZ2VuZXMgPC0gc3Vic2V0KGZ1bGxfcmVzdWx0c19nZW5lcywgc2VsZWN0ID0gLWMoZ2VuZSkpCgpmb3IgKHJvdyBpbiAxOm5yb3coZnVsbF9yZXN1bHRzX2dlbmVzKSl7CiAgbnVtX3BhaXJzX3NpZ19yZWd1bGF0ZWQgPSAwCiAgbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkID0gMAogIG51bV9wYWlyc19zaWdfdXByZWd1bGF0ZWQgPSAwCiAgZm9yIChwYWlyIGluIHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpIHsKICAgIHBhZGogPSBmdWxsX3Jlc3VsdHNfZ2VuZXNbcm93LCBzdHJfaW50ZXJwKCIke3BhaXJ9X3BhZGoiKV0KICAgIGwyZmMgPSBmdWxsX3Jlc3VsdHNfZ2VuZXNbcm93LCBzdHJfaW50ZXJwKCIke3BhaXJ9X2wyZmMiKV0KICAgIGlmICghaXMubmEocGFkaikgJiYgcGFkaiA8IDAuMDUpIHsKICAgICAgaWYgKGwyZmMgPCAwKSB7CiAgICAgICAgbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkID0gbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkICsgMQogICAgICB9IGVsc2UgaWYgKGwyZmMgPiAwKSB7CiAgICAgICAgbnVtX3BhaXJzX3NpZ191cHJlZ3VsYXRlZCA9IG51bV9wYWlyc19zaWdfdXByZWd1bGF0ZWQgKyAxCiAgICAgIH0KICAgIH0KICB9CiAgZnVsbF9yZXN1bHRzX2dlbmVzW3JvdywgIm51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkIl0gPSBudW1fcGFpcnNfc2lnX3VwcmVndWxhdGVkCiAgZnVsbF9yZXN1bHRzX2dlbmVzW3JvdywgIm51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQiXSA9IG51bV9wYWlyc19zaWdfZG93bnJlZ3VsYXRlZAogIGZ1bGxfcmVzdWx0c19nZW5lc1tyb3csICJudW1fcGFpcnNfc2lnX3JlZ3VsYXRlZCJdID0gbnVtX3BhaXJzX3NpZ191cHJlZ3VsYXRlZCArIG51bV9wYWlyc19zaWdfZG93bnJlZ3VsYXRlZAp9CgpmdWxsX3Jlc3VsdHNfZ2VuZXMgPSBmdWxsX3Jlc3VsdHNfZ2VuZXMgJT4lIAogIHJlbG9jYXRlKG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQpJT4lIAogIHJlbG9jYXRlKG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkKSAlPiUgCiAgcmVsb2NhdGUobnVtX3BhaXJzX3NpZ19yZWd1bGF0ZWQpCmZ1bGxfcmVzdWx0c19nZW5lcyA9IGZ1bGxfcmVzdWx0c19nZW5lc1tvcmRlcihmdWxsX3Jlc3VsdHNfZ2VuZXMkbnVtX3BhaXJzX3NpZ19yZWd1bGF0ZWQsIGRlY3JlYXNpbmcgPSBUUlVFKSxdCgpwcmludChmdWxsX3Jlc3VsdHNfZ2VuZXMpCndyaXRlLmNzdihmdWxsX3Jlc3VsdHNfZ2VuZXMsIGZpbGUgPSAiZGVzZXEvb3V0cHV0L2RpZmZlcmVudGlhbF9nZW5lX2V4cHJlc3Npb25fYWxsX3NlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMuY3N2IikKYGBgCgpDb21iaW5lIGRpZmZlcmVudGlhbCBwYXRod2F5IGRhdGEKCmBgYHtyfQojIyMgQ29tYmluZSByZXN1bHRzIGFjcm9zcyBwYWlycwpmdWxsX3Jlc3VsdHNfcGF0aHdheXMgPC0gZGF0YS5mcmFtZSgpCmZvciAocGFpciBpbiBzZW5zaXRpdmVfcmVzaXN0YW50X3BhaXJzKSB7CiAgcGFpcl91cF9wYXRod2F5c19maWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfdXByZWd1bGF0ZWRfcGF0aHdheXMuY3N2IikKICBwYWlyX3VwX3BhdGh3YXlzIDwtIGFzLmRhdGEuZnJhbWUocmVhZC5jc3YocGFpcl91cF9wYXRod2F5c19maWxlLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpKQogIAogIHBhaXJfZG93bl9wYXRod2F5c19maWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfZG93bnJlZ3VsYXRlZF9wYXRod2F5cy5jc3YiKQogIHBhaXJfZG93bl9wYXRod2F5cyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KHBhaXJfZG93bl9wYXRod2F5c19maWxlLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpKQogIAogIHBhaXJfcGF0aHdheXMgPSByYmluZChwYWlyX3VwX3BhdGh3YXlzLCBwYWlyX2Rvd25fcGF0aHdheXMpCiAgcGFpcl9wYXRod2F5cyRJRCA9IHJvd25hbWVzKHBhaXJfcGF0aHdheXMpCiAgcGFpcl9wYXRod2F5c19mb3JtYXR0ZWQgPSBwYWlyX3BhdGh3YXlzWywgYygiSUQiLCAiRGVzY3JpcHRpb24iLCAicC5hZGp1c3QiLCAiTkVTIildCgogIGNvbG5hbWVzKHBhaXJfcGF0aHdheXNfZm9ybWF0dGVkKSA8LSBjKCJJRCIsICJEZXNjcmlwdGlvbiIsIHN0cl9pbnRlcnAoIiR7cGFpcn1fcGFkaiIpLCBzdHJfaW50ZXJwKCIke3BhaXJ9X05FUyIpKQogIAogIGlmIChuY29sKGZ1bGxfcmVzdWx0c19wYXRod2F5cykgPT0gMCkgewogICAgZnVsbF9yZXN1bHRzX3BhdGh3YXlzID0gcGFpcl9wYXRod2F5c19mb3JtYXR0ZWQKICB9IGVsc2UgewogICAgZnVsbF9yZXN1bHRzX3BhdGh3YXlzIDwtIG1lcmdlKGZ1bGxfcmVzdWx0c19wYXRod2F5cywgcGFpcl9wYXRod2F5c19mb3JtYXR0ZWQsIGJ5ID0gYygiSUQiLCAiRGVzY3JpcHRpb24iKSwgYWxsID0gVFJVRSkKICB9Cn0KCnJvd25hbWVzKGZ1bGxfcmVzdWx0c19wYXRod2F5cykgPC0gZnVsbF9yZXN1bHRzX3BhdGh3YXlzJElECmZ1bGxfcmVzdWx0c19wYXRod2F5cyA8LSBzdWJzZXQoZnVsbF9yZXN1bHRzX3BhdGh3YXlzLCBzZWxlY3QgPSAtYyhJRCkpCgpmb3IgKHJvdyBpbiAxOm5yb3coZnVsbF9yZXN1bHRzX3BhdGh3YXlzKSl7CiAgbnVtX3BhaXJzX3NpZ19yZWd1bGF0ZWQgPSAwCiAgbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkID0gMAogIG51bV9wYWlyc19zaWdfdXByZWd1bGF0ZWQgPSAwCiAgZm9yIChwYWlyIGluIHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpIHsKICAgIHBhZGogPSBmdWxsX3Jlc3VsdHNfcGF0aHdheXNbcm93LCBzdHJfaW50ZXJwKCIke3BhaXJ9X3BhZGoiKV0KICAgIE5FUyA9IGZ1bGxfcmVzdWx0c19wYXRod2F5c1tyb3csIHN0cl9pbnRlcnAoIiR7cGFpcn1fTkVTIildCiAgICBpZiAoIWlzLm5hKHBhZGopICYmIHBhZGogPCAwLjA1KSB7CiAgICAgIGlmIChORVMgPCAwKSB7CiAgICAgICAgbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkID0gbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkICsgMQogICAgICB9IGVsc2UgaWYgKE5FUyA+IDApIHsKICAgICAgICBudW1fcGFpcnNfc2lnX3VwcmVndWxhdGVkID0gbnVtX3BhaXJzX3NpZ191cHJlZ3VsYXRlZCArIDEKICAgICAgfQogICAgfQogIH0KICBmdWxsX3Jlc3VsdHNfcGF0aHdheXNbcm93LCAibnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQiXSA9IG51bV9wYWlyc19zaWdfdXByZWd1bGF0ZWQKICBmdWxsX3Jlc3VsdHNfcGF0aHdheXNbcm93LCAibnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCJdID0gbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkCiAgZnVsbF9yZXN1bHRzX3BhdGh3YXlzW3JvdywgIm51bV9wYWlyc19zaWdfcmVndWxhdGVkIl0gPSBudW1fcGFpcnNfc2lnX3VwcmVndWxhdGVkICsgbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkCn0KCmZ1bGxfcmVzdWx0c19wYXRod2F5cyA9IGZ1bGxfcmVzdWx0c19wYXRod2F5cyAlPiUgCiAgcmVsb2NhdGUobnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCklPiUgCiAgcmVsb2NhdGUobnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQpICU+JSAKICByZWxvY2F0ZShudW1fcGFpcnNfc2lnX3JlZ3VsYXRlZCkKZnVsbF9yZXN1bHRzX3BhdGh3YXlzID0gZnVsbF9yZXN1bHRzX3BhdGh3YXlzW29yZGVyKGZ1bGxfcmVzdWx0c19wYXRod2F5cyRudW1fcGFpcnNfc2lnX3JlZ3VsYXRlZCwgZGVjcmVhc2luZyA9IFRSVUUpLF0KCnByaW50KGZ1bGxfcmVzdWx0c19wYXRod2F5cykKd3JpdGUuY3N2KGZ1bGxfcmVzdWx0c19wYXRod2F5cywgZmlsZSA9ICJkZXNlcS9vdXRwdXQvZGlmZmVyZW50aWFsX3BhdGh3YXlzX2FsbF9zZW5zaXRpdmVfcmVzaXN0YW50X3BhaXJzLmNzdiIpCmBgYAoKIyMjIFBsb3QgZGlmZmVyZW50aWFsIHBhdGh3YXlzCgpVc2UgUkVWSUdPIHRvIGNsdXN0ZXIgcGF0aHdheXMKCmBgYHtyfQoKc2ltaWxhcml0eV9tYXRyaXggPSBjYWxjdWxhdGVTaW1NYXRyaXgocm93bmFtZXMoZnVsbF9yZXN1bHRzX3BhdGh3YXlzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnZGIgPSAib3JnLkhzLmVnLmRiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb250ID0gIkJQIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gIlJlbCIpCgpzY29yZXMgPSByZXAoMSwgbmNvbChzaW1pbGFyaXR5X21hdHJpeCkpCm5hbWVzKHNjb3JlcykgPSBjb2xuYW1lcyhzaW1pbGFyaXR5X21hdHJpeCkKcmVkdWNlZFRlcm1zID0gcmVkdWNlU2ltTWF0cml4KHNpbWlsYXJpdHlfbWF0cml4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NvcmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhyZXNob2xkID0gMC44LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnZGIgPSAib3JnLkhzLmVnLmRiIikKYGBgCgpQbG90IGNvbnNpc3RlbnRseSBkaWZmZXJlbnRpYWwgcGF0aHdheXMgZm9yIGFsbCBjZWxsbGluZXMgaW4gb25lIHBsb3QKCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9OH0KZGFya2VuX2NvbG9yIDwtIGZ1bmN0aW9uKGNvbG9yLCBmYWN0b3IgPSAwLjUpIHsKICAjIENvbnZlcnQgY29sb3IgdG8gUkdCCiAgcmdiX3ZhbHMgPC0gY29sMnJnYihjb2xvcikgLyAyNTUKICAjIERhcmtlbiBlYWNoIFJHQiBjaGFubmVsCiAgZGFya2VuZWRfdmFscyA8LSByZ2JfdmFscyAqIGZhY3RvcgogICMgRW5zdXJlIHZhbHVlcyBhcmUgd2l0aGluIHZhbGlkIHJhbmdlIFswLCAxXQogIGRhcmtlbmVkX3ZhbHMgPC0gcG1pbihwbWF4KGRhcmtlbmVkX3ZhbHMsIDApLCAxKQogICMgQ29udmVydCBiYWNrIHRvIGhleGFkZWNpbWFsIGNvbG9yCiAgZGFya2VuZWRfY29sb3IgPC0gcmdiKGRhcmtlbmVkX3ZhbHNbMV0sIGRhcmtlbmVkX3ZhbHNbMl0sIGRhcmtlbmVkX3ZhbHNbM10sIG1heENvbG9yVmFsdWUgPSAyNTUpCiAgcmV0dXJuKGRhcmtlbmVkX2NvbG9yKQp9CgoKZm9ybWF0X3BhdGh3YXlfcmVzdWx0cyA9IGZ1bGxfcmVzdWx0c19wYXRod2F5cwojIFN1YnNldCB0byB0ZXJtcyBhYmxlIHRvIGJlIGNsdXN0ZXJlZApmb3JtYXRfcGF0aHdheV9yZXN1bHRzID0gZm9ybWF0X3BhdGh3YXlfcmVzdWx0c1tyb3duYW1lcyhmb3JtYXRfcGF0aHdheV9yZXN1bHRzKSAlaW4lIHJvd25hbWVzKHJlZHVjZWRUZXJtcyksXQpmb3JtYXRfcGF0aHdheV9yZXN1bHRzID0gZm9ybWF0X3BhdGh3YXlfcmVzdWx0c1ssLSB3aGljaChjb2xuYW1lcyhmb3JtYXRfcGF0aHdheV9yZXN1bHRzKSAlaW4lIGMoIm51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQiLCAibnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQiKSldCgojIENoYW5nZSBmb3JtYXR0aW5nIG9mIGNvbHVtbiBuYW1lcyBzbyB0aGF0IGl0IGlzIGVhc2llciBmb3IgcGl2b3QgZnVuY3Rpb24gYmVsb3cKY29sbmFtZXMoZm9ybWF0X3BhdGh3YXlfcmVzdWx0cykgPSBjb2xuYW1lcyhmb3JtYXRfcGF0aHdheV9yZXN1bHRzKSAlPiUKICBtYXBfY2hyKFwoeCkgc3ViKCJfKD89W15fXSokKSIsICItIiwgeCwgcGVybCA9IFRSVUUpKQoKIyBTZXQgcGFyZW50IHRlcm1zCmZvcm1hdF9wYXRod2F5X3Jlc3VsdHMkcGFyZW50UGF0aHdheUlkID0gcm93bmFtZXMoZm9ybWF0X3BhdGh3YXlfcmVzdWx0cykgJT4lCiAgbWFwX2NocihcKHgpIHJlZHVjZWRUZXJtc1tyb3duYW1lcyhyZWR1Y2VkVGVybXMpID09IHgsICJwYXJlbnQiXSkKZm9ybWF0X3BhdGh3YXlfcmVzdWx0cyRwYXJlbnRQYXRod2F5RGVzY3JpcHRpb24gPSBmb3JtYXRfcGF0aHdheV9yZXN1bHRzJHBhcmVudFBhdGh3YXlJZCAlPiUKICBtYXBfY2hyKFwoeCkgcGFzdGUwKGZvcm1hdF9wYXRod2F5X3Jlc3VsdHNbcm93bmFtZXMoZm9ybWF0X3BhdGh3YXlfcmVzdWx0cykgPT0geCwgIkRlc2NyaXB0aW9uIl0sICIgKCIsIG5yb3coZm9ybWF0X3BhdGh3YXlfcmVzdWx0c1tmb3JtYXRfcGF0aHdheV9yZXN1bHRzJHBhcmVudFBhdGh3YXlJZCA9PSB4LF0pLCAiKSIpKQoKcGl2b3RfY29scyA9IGNvbG5hbWVzKGZvcm1hdF9wYXRod2F5X3Jlc3VsdHMpCnBpdm90X2NvbHMgPSBwaXZvdF9jb2xzWyEocGl2b3RfY29scyAlaW4lIGMoIkRlc2NyaXB0aW9uIiwgIm51bV9wYWlyc19zaWctcmVndWxhdGVkIiwgInBhcmVudFBhdGh3YXlJZCIsICJwYXJlbnRQYXRod2F5RGVzY3JpcHRpb24iKSldCnBsb3RfZGF0YSA9IHBpdm90X2xvbmdlcihkYXRhID0gZm9ybWF0X3BhdGh3YXlfcmVzdWx0cywKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHMgPSBwaXZvdF9jb2xzLAogICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSBjKCJjZWxsbGluZVBhaXIiLCAiLnZhbHVlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lc19zZXAgPSAiLSIpCgojIFNjb3JlIHRoZSB0ZXJtIGNhdGVnb3JpZXMgYnkgY29tYmluaW5nIHNjb3JlcyBhY3Jvc3MgYWxsIGNoaWxkIHBhdGh3YXlzIGFuZCBhbGwgY2VsbGxpbmUgcGFpcnMKcGxvdF9kYXRhJHNjb3JlID0gcGxvdF9kYXRhJE5FUwojIHBsb3RfZGF0YSRzY29yZSA9IC1sb2cxMChwbG90X2RhdGEkcGFkaikgKiBwbG90X2RhdGEkTkVTCnBsb3RfZGF0YSA9IHBsb3RfZGF0YSAlPiUKICBncm91cF9ieShwYXJlbnRQYXRod2F5SWQpICU+JQogIG11dGF0ZShjYXRlZ29yeV9zY29yZSA9IG1lYW4oc2NvcmUsIG5hLnJtID0gVFJVRSkpCgojIE9yZGVyIHRlcm1zIGJ5IHRoZSBzY29yZSBvZiB0aGUgcGFyZW50IHBhdGh3YXkKcGxvdF9kYXRhID0gcGxvdF9kYXRhICU+JSBhcnJhbmdlKGNhdGVnb3J5X3Njb3JlLCBzY29yZSkKcGxvdF9kYXRhJHBhcmVudFBhdGh3YXlEZXNjcmlwdGlvbiA8LSBmYWN0b3IocGxvdF9kYXRhJHBhcmVudFBhdGh3YXlEZXNjcmlwdGlvbiwgbGV2ZWxzID0gdW5pcXVlKHBsb3RfZGF0YSRwYXJlbnRQYXRod2F5RGVzY3JpcHRpb24pKQoKIyBTaGlmdCBORVMgc28gdGhhdCB0aGUgLTEgdG8gMSByZWdpb24gZG9lc24ndCBnbyB1bnVzZWQKaWYgKGFueShhYnMocGxvdF9kYXRhJE5FUykpIDwgMSkgewogIHByaW50KCJFUlJPUjogVGhpcyBwbG90IHdpbGwgYmUgbWVzc2VkIHVwIGJlY2F1c2Ugd2UgYXNzdW1lZCBub3ZhbHVlcyBvZiBORVMgYmV0d2VlbiAtMSBhbmQgMS4iKQp9CiMgcGxvdF9kYXRhJHNoaWZ0X05FUyA9IHBsb3RfZGF0YSRORVMgJT4lCiMgICBtYXBfZGJsKFwoeCkgaWZlbHNlKHggPiAxLCB4IC0gMSwgaWZlbHNlKHggPCAtMSwgeCArIDEsIHgpKSkKCmdncGxvdChwbG90X2RhdGEsIGFlcyh4ID0gTkVTLCB5ID0gcGFyZW50UGF0aHdheURlc2NyaXB0aW9uLCBzaXplID0gLWxvZzEwKHBhZGopLCBjb2xvciA9IGNlbGxsaW5lUGFpcikpICsKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKGhlaWdodCA9IDAuMSksIGFscGhhID0gMC41LCBsYXllciA9ICJhYm92ZSIpICsgICMgQWRkIGppdHRlciBhbmQgcmVkdWNlIHBvaW50IHRyYW5zcGFyZW5jeQogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMSwgNSkpICsKICAjIENvbG9yaW5nIGJ5IGluIHZpdm8vaW4gdml0cm8gcmVzaXN0YW5jZSAoT1ZDQVJzIGJvdGggaW4gdml0cm87IFBFTyBhbmQgUEVBcyBpbiB2aXZvKQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjRjcxMzAwIiwgIiNFN0NGMDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjRjU1QUZBIiwgIiNCRDgxNjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjMTFEQkNDIiwgIiMwRTU4RTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjMTRCNzBBIikpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBzZXFfYWxvbmcobGV2ZWxzKHBsb3RfZGF0YSRwYXJlbnRQYXRod2F5RGVzY3JpcHRpb24pKSwgY29sb3IgPSAiZ3JheSIsIHNpemUgPSAwLjEsIGxheWVyID0gImJlbG93IikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIikgKwoKICBsYWJzKHggPSAnTm9ybWFsaXplZCBFbnJpY2htZW50IFNjb3JlJywgeSA9ICdQYXRod2F5IENhdGVnb3J5ICgjIHBhdGh3YXlzKScsIGNvbG9yID0gJ0NlbGxsaW5lIFBhaXInKSArCiAgZ2d0aXRsZSgnUGF0aHdheSByZWd1bGF0aW9uIGluIHJlc2lzdGFudCBjZWxsbGluZXMgKGFzIGNvbXBhcmVkIHRvIGNvcnJlc3BvbmRpbmcgc2Vuc2l0aXZlIGNlbGxsaW5lKScpICsKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZV9jbGFzc2ljKCkKCmdnc2F2ZSgiZGVzZXEvb3V0cHV0L2RpZmZlcmVudGlhbF9wYXRod2F5c19hbGxfY2VsbGxpbmVzX3NpbWlsYXJpdHlfdGhyZXNob2xkXzAuOC5zdmciLAogICAgICAgcGxvdCA9IGxhc3RfcGxvdCgpLAogICAgICAgZGV2aWNlID0gInN2ZyIsCiAgICAgICB3aWR0aCA9IDE1LAogICAgICAgaGVpZ2h0ID0gMTAsCiAgICAgICB1bml0cyA9ICJpbiIpCgojIyBwbG90IGluIHZpdHJvIHJlc2lzdGFuY2Ugb25seQpnZ3Bsb3QocGxvdF9kYXRhW3Bsb3RfZGF0YSRjZWxsbGluZVBhaXIgJWluJSBjKCJPVkNBUjRCX3ZzX09WQ0FSNCIsICJPVkNBUjRBX3ZzX09WQ0FSNCIsICJPVkNBUjNBX3ZzX09WQ0FSMyIsICJPVkNBUjNCX3ZzX09WQ0FSMyIpLF0sIGFlcyh4ID0gTkVTLCB5ID0gcGFyZW50UGF0aHdheURlc2NyaXB0aW9uLCBzaXplID0gLWxvZzEwKHBhZGopLCBjb2xvciA9IGNlbGxsaW5lUGFpcikpICsKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKGhlaWdodCA9IDAuMSksIGFscGhhID0gMC41LCBsYXllciA9ICJhYm92ZSIpICsgICMgQWRkIGppdHRlciBhbmQgcmVkdWNlIHBvaW50IHRyYW5zcGFyZW5jeQogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMSwgNSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiI0Y3MTMwMCIsICIjRTdDRjAwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiI0Y1NUFGQSIsICIjQkQ4MTYxIikpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBzZXFfYWxvbmcobGV2ZWxzKHBsb3RfZGF0YSRwYXJlbnRQYXRod2F5RGVzY3JpcHRpb24pKSwgY29sb3IgPSAiZ3JheSIsIHNpemUgPSAwLjEsIGxheWVyID0gImJlbG93IikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIikgKwoKICBsYWJzKHggPSAnTm9ybWFsaXplZCBFbnJpY2htZW50IFNjb3JlJywgeSA9ICdQYXRod2F5IENhdGVnb3J5ICgjIHBhdGh3YXlzKScsIGNvbG9yID0gJ0NlbGxsaW5lIFBhaXInKSArCiAgZ2d0aXRsZSgnUGF0aHdheSByZWd1bGF0aW9uIGluIGluLXZpdHJvIGRlcml2ZWQgcmVzaXN0YW50IGNlbGxsaW5lcycpICsKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZV9jbGFzc2ljKCkKCiMjIHBsb3QgaW4gdml2byByZXNpc3RhbmNlIG9ubHkKZ2dwbG90KHBsb3RfZGF0YVtwbG90X2RhdGEkY2VsbGxpbmVQYWlyICVpbiUgYygiUEVPNF92c19QRU8yIiwgIlBFTzZfdnNfUEVPMiIsICJQRUEyX3ZzX1BFQTEiKSxdLCBhZXMoeCA9IE5FUywgeSA9IHBhcmVudFBhdGh3YXlEZXNjcmlwdGlvbiwgc2l6ZSA9IC1sb2cxMChwYWRqKSwgY29sb3IgPSBjZWxsbGluZVBhaXIpKSArCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcihoZWlnaHQgPSAwLjEpLCBhbHBoYSA9IDAuNSwgbGF5ZXIgPSAiYWJvdmUiKSArICAjIEFkZCBqaXR0ZXIgYW5kIHJlZHVjZSBwb2ludCB0cmFuc3BhcmVuY3kKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDEsIDUpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiMxMURCQ0MiLCAiIzBFNThFNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiMxNEI3MEEiKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHNlcV9hbG9uZyhsZXZlbHMocGxvdF9kYXRhJHBhcmVudFBhdGh3YXlEZXNjcmlwdGlvbikpLCBjb2xvciA9ICJncmF5Iiwgc2l6ZSA9IDAuMSwgbGF5ZXIgPSAiYmVsb3ciKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmxhY2siKSArCgogIGxhYnMoeCA9ICdOb3JtYWxpemVkIEVucmljaG1lbnQgU2NvcmUnLCB5ID0gJ1BhdGh3YXkgQ2F0ZWdvcnkgKCMgcGF0aHdheXMpJywgY29sb3IgPSAnQ2VsbGxpbmUgUGFpcicpICsKICBnZ3RpdGxlKCdQYXRod2F5IHJlZ3VsYXRpb24gaW4gaW4tdml2byBkZXJpdmVkIHJlc2lzdGFudCBjZWxsbGluZXMnKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWVfY2xhc3NpYygpCgpgYGAKIyMjIFBsb3QgZGlmZmVyZW50aWFsIHBhdGh3YXlzCgpQbG90IHNob3dzIGhvdyB0aGUgc3ByZWFkIG9mIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsIHBhdGh3YXlzIGFjcm9zcyBzZW5zaXRpdmUvcmVzaXN0YW50IHBhaXJzLgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNX0KcGxvdF9kYXRhID0gZnVsbF9yZXN1bHRzX3BhdGh3YXlzWyxjKCJudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCIsICJudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkIiwgIkRlc2NyaXB0aW9uIildCnByaW50KHBsb3RfZGF0YSkKIyBPcmRlcnMgcHJpbWFyaWx5IGJ5IGhvdyBtYW55IG1vcmUgcGFpcnMgd2VyZSBzaWcgdXAgdGhhbiBzaWcgZG93biByZWd1bGF0ZWQKIyBPcmRlcnMgc2Vjb25kYXJpbHkgYnkgaG93IGZldyBwYWlycyB3ZXJlIHJlZ3VsYXRlZCBpbiB0aGUgbm9uLWRvbWluYW50IGRpcmVjdGlvbgojIFRoaXMgb3JkZXJpbmcgZG9lcyB0aGUgZm9sbG93aW5nOiAoNywgMCkgPiAoNiwgMCkgPiAoNSwgMCkgPiAoNiwgMSkgPiAoNywgMikKcGxvdF9kYXRhIDwtIHBsb3RfZGF0YSAlPiUKICBtdXRhdGUob3JkZXIgPSBpZmVsc2UobnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQgPiBudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkLAogICAgICAgICAgICAgICAgICAgICAgICBudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCAtIG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQgKiAxLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAtMSAqIChudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkIC0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQgKiAxLjAxKSkpCgpwbG90X2RhdGEgPC0gcGxvdF9kYXRhICU+JQogIGFycmFuZ2UoZGVzYyhvcmRlcikpCgpwbG90X2RhdGEgPSBwbG90X2RhdGEgJT4lCiAgbXV0YXRlKHBhbmVsID0gaWZlbHNlKG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkID4gbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwKICAgICAgICAgICAgICAgICAgICAgICAgIm92ZXJhbGwgdXByZWd1bGF0ZWQiLAogICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UobnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCA9PSBudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJldmVubHkgdXAgYW5kIGRvd24gcmVndWxhdGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvdmVyYWxsIGRvd25yZWd1bGF0ZWQiKSkpCnBsb3RfZGF0YSRwYW5lbCA9IGZhY3RvcihwbG90X2RhdGEkcGFuZWwsIGxldmVscyA9IGMoIm92ZXJhbGwgdXByZWd1bGF0ZWQiLCAiZXZlbmx5IHVwIGFuZCBkb3duIHJlZ3VsYXRlZCIsICJvdmVyYWxsIGRvd25yZWd1bGF0ZWQiKSkKCiMgb3JnYW5pemUgdGhlIHggY29vcmRpbmF0ZXMgYmFzZWQgb24gcGFuZWwKcGxvdF9kYXRhJGxlZnRfcGxhY2VtZW50ID0gMDoobnJvdyhwbG90X2RhdGEpIC0gMSkKZXF1YWxfcGFuZWxfc3RhcnQgPSBtaW4ocGxvdF9kYXRhW3Bsb3RfZGF0YSRwYW5lbCA9PSAiZXZlbmx5IHVwIGFuZCBkb3duIHJlZ3VsYXRlZCIsICJsZWZ0X3BsYWNlbWVudCJdKQpwbG90X2RhdGFbcGxvdF9kYXRhJHBhbmVsID09ICJldmVubHkgdXAgYW5kIGRvd24gcmVndWxhdGVkIixdJGxlZnRfcGxhY2VtZW50ID0gcGxvdF9kYXRhW3Bsb3RfZGF0YSRwYW5lbCA9PSAiZXZlbmx5IHVwIGFuZCBkb3duIHJlZ3VsYXRlZCIsXSRsZWZ0X3BsYWNlbWVudCAtIGVxdWFsX3BhbmVsX3N0YXJ0CmRvd25fcGFuZWxfc3RhcnQgPSBtaW4ocGxvdF9kYXRhW3Bsb3RfZGF0YSRwYW5lbCA9PSAib3ZlcmFsbCBkb3ducmVndWxhdGVkIiwgImxlZnRfcGxhY2VtZW50Il0pCnBsb3RfZGF0YVtwbG90X2RhdGEkcGFuZWwgPT0gIm92ZXJhbGwgZG93bnJlZ3VsYXRlZCIsXSRsZWZ0X3BsYWNlbWVudCA9IHBsb3RfZGF0YVtwbG90X2RhdGEkcGFuZWwgPT0gIm92ZXJhbGwgZG93bnJlZ3VsYXRlZCIsXSRsZWZ0X3BsYWNlbWVudCAtIGRvd25fcGFuZWxfc3RhcnQKCnBsb3RfZGF0YSRjZW50ZXJfcGxhY2VtZW50ID0gcGxvdF9kYXRhJGxlZnRfcGxhY2VtZW50ICsgMC41CgojIENyZWF0ZSBiYXIgcGxvdApnZ3Bsb3QocGxvdF9kYXRhKSArCiAgZmFjZXRfd3JhcCh+cGFuZWwsIG5jb2wgPSAzKSArCiAgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gLTEgKiBudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkLCBmaWxsID0gImRvd25yZWd1bGF0aW9uIiwgd2lkdGggPSAxKSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX2JhcihhZXMoeCA9IGNlbnRlcl9wbGFjZW1lbnQsIHkgPSBudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCwgZmlsbCA9ICJ1cHJlZ3VsYXRpb24iLCB3aWR0aCA9IDEpLCBzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC1tYXgocGxvdF9kYXRhJG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQpIC0xLCBtYXgocGxvdF9kYXRhJG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkKSkgKzEpICsKICB0aGVtZV9taW5pbWFsKCkgKyAgIyBBcHBseSBhIG1pbmltYWwgdGhlbWUKICBsYWJzKHggPSAiUGF0aHdheXMiLCB5ID0gIiMgcmVzaXN0YW50IGNlbGxsaW5lcyBzaG93aW5nIHNpZ25pZmljYW50IHJlZ3VsYXRpb24gb2YgcGF0aHdheSIpICsgIyBTZXQgYXhpcyBsYWJlbHMKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJ1cHJlZ3VsYXRpb24iID0gImdyZWVuIiwgImRvd25yZWd1bGF0aW9uIiA9ICJyZWQiKSkgKyAjIFNldCBmaWxsIGNvbG9ycwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGdndGl0bGUoJ1BhdGh3YXkgcmVndWxhdGlvbiBpbiByZXNpc3RhbnQgY2VsbGxpbmVzIChhcyBjb21wYXJlZCB0byBjb3JyZXNwb25kaW5nIHNlbnNpdGl2ZSBjZWxsbGluZSknKQogIAoKZ2dzYXZlKCJkZXNlcS9vdXRwdXQvY29uc2lzdGVudGx5X2RpZmZlcmVudGlhbF9wYXRod2F5c19hbGxfY2VsbGxpbmVzLnN2ZyIsCiAgICAgICBwbG90ID0gbGFzdF9wbG90KCksCiAgICAgICBkZXZpY2UgPSAic3ZnIiwKICAgICAgIHdpZHRoID0gMTAsCiAgICAgICBoZWlnaHQgPSA1LAogICAgICAgdW5pdHMgPSAiaW4iKQpgYGAKCkNsb3NlIHVwIG9mIGNvbnNpc3RlbnRseSB1cHJlZ3VsYXRlZCBwYXRod2F5cwoKYGBge3IsIGZpZ3VyZS53aWR0aCA9IDEwLCBmaWd1cmUuaGVpZ2h0ID0gNX0KIyBFbmQgdGhlIHBsb3QgYmV0d2VlbiAiY2F0ZWdvcmllcyIgb2YgcGF0aHdheXMsIGluY2x1ZGluZyBsZXNzIHRoYW4gMTAwIHBhdGh3YXlzIHRvdGFsCmVuZF9pbmRleCA9IDEKZm9yKGkgaW4gMToxMDApIHsKICBpZiAocGxvdF9kYXRhJG9yZGVyW2ldICE9IHBsb3RfZGF0YSRvcmRlcltpKzFdKSB7CiAgICBlbmRfaW5kZXggPSBpCiAgfQp9CgpwbG90X2RhdGFfdXAgPSBwbG90X2RhdGFbMTplbmRfaW5kZXgsXQpwbG90X2RhdGFfdXAkcGF0aHdheSA9IHBsb3RfZGF0YV91cCREZXNjcmlwdGlvbgoKcHJpbnQocGxvdF9kYXRhX3VwKQoKZ2dwbG90KHBsb3RfZGF0YV91cCkgKwogIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IC0xICogbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwgZmlsbCA9ICJkb3ducmVndWxhdGlvbiIsIHdpZHRoID0gMSksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQsIGZpbGwgPSAidXByZWd1bGF0aW9uIiwgd2lkdGggPSAxKSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gMSwgbGFiZWwgPSBwYXRod2F5KSwgc2l6ZSA9IDIsIGNvbG9yID0gImJsYWNrIiwgYW5nbGUgPSA5MCkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC1tYXgocGxvdF9kYXRhX3VwJG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQpIC0xLCBtYXgocGxvdF9kYXRhX3VwJG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkKSkgKzEpICsKICB0aGVtZV9taW5pbWFsKCkgKyAgIyBBcHBseSBhIG1pbmltYWwgdGhlbWUKICBsYWJzKHggPSAiUGF0aHdheXMiLCB5ID0gIiMgcmVzaXN0YW50IGNlbGxsaW5lcyBzaG93aW5nIHNpZ25pZmljYW50IHJlZ3VsYXRpb24gb2YgcGF0aHdheSIpICsgIyBTZXQgYXhpcyBsYWJlbHMKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJ1cHJlZ3VsYXRpb24iID0gImdyZWVuIiwgImRvd25yZWd1bGF0aW9uIiA9ICJyZWQiKSkgKyAjIFNldCBmaWxsIGNvbG9ycwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGdndGl0bGUoJ1pvb21pbmcgaW4gb24gbGVmdCBoYW5kIHNpZGUgKGV4dHJlbWVseSBjb25zaXN0ZW50bHkgdXByZWd1bGF0ZWQgcGF0aHdheXMpJykKCmdnc2F2ZSgiZGVzZXEvb3V0cHV0L2NvbnNpc3RlbnRseV91cHJlZ3VsYXRlZF9wYXRod2F5c19hbGxfY2VsbGxpbmVzLnN2ZyIsCiAgICAgICBwbG90ID0gbGFzdF9wbG90KCksCiAgICAgICBkZXZpY2UgPSAic3ZnIiwKICAgICAgIHdpZHRoID0gMTAsCiAgICAgICBoZWlnaHQgPSA1LAogICAgICAgdW5pdHMgPSAiaW4iKQpgYGAKCkNsb3NlIHVwIG9mIGNvbnNpc3RlbnRseSBkb3ducmVndWxhdGVkIHBhdGh3YXlzCgpgYGB7ciwgZmlndXJlLndpZHRoID0gMTAsIGZpZ3VyZS5oZWlnaHQgPSA1fQojIEVuZCB0aGUgcGxvdCBiZXR3ZWVuICJjYXRlZ29yaWVzIiBvZiBwYXRod2F5cywgaW5jbHVkaW5nIGxlc3MgdGhhbiAxMDAgcGF0aHdheXMgdG90YWwKc3RhcnRfaW5kZXggPSAxCmZvcihpIGluIG5yb3cocGxvdF9kYXRhKToobnJvdyhwbG90X2RhdGEpIC0gOTkpKSB7CiAgaWYgKHBsb3RfZGF0YSRvcmRlcltpXSAhPSBwbG90X2RhdGEkb3JkZXJbaS0xXSkgewogICAgc3RhcnRfaW5kZXggPSBpCiAgfQp9CgpwbG90X2RhdGFfZG93biA9IHBsb3RfZGF0YVtzdGFydF9pbmRleDpucm93KHBsb3RfZGF0YSksXQpwbG90X2RhdGFfZG93biRjZW50ZXJfcGxhY2VtZW50ID0gcGxvdF9kYXRhX2Rvd24kY2VudGVyX3BsYWNlbWVudCAtIHN0YXJ0X2luZGV4ICsgMSAjIFN0YXJ0IGF0IDAKcGxvdF9kYXRhX2Rvd24kcGF0aHdheSA9IHBsb3RfZGF0YV9kb3duJERlc2NyaXB0aW9uCgpwcmludChwbG90X2RhdGFfZG93bikKCmdncGxvdChwbG90X2RhdGFfZG93bikgKwogIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IC0xICogbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwgZmlsbCA9ICJkb3ducmVndWxhdGlvbiIsIHdpZHRoID0gMSksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQsIGZpbGwgPSAidXByZWd1bGF0aW9uIiwgd2lkdGggPSAxKSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gLTEsIGxhYmVsID0gcGF0aHdheSksIHNpemUgPSAyLCBjb2xvciA9ICJibGFjayIsIGFuZ2xlID0gOTApICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygtbWF4KHBsb3RfZGF0YV9kb3duJG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQpIC0xLCBtYXgocGxvdF9kYXRhX2Rvd24kbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQpKSArMSkgKwogIHRoZW1lX21pbmltYWwoKSArICAjIEFwcGx5IGEgbWluaW1hbCB0aGVtZQogIGxhYnMoeCA9ICJQYXRod2F5cyIsIHkgPSAiIyByZXNpc3RhbnQgY2VsbGxpbmVzIHNob3dpbmcgc2lnbmlmaWNhbnQgcmVndWxhdGlvbiBvZiBwYXRod2F5IikgKyAjIFNldCBheGlzIGxhYmVscwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInVwcmVndWxhdGlvbiIgPSAiZ3JlZW4iLCAiZG93bnJlZ3VsYXRpb24iID0gInJlZCIpKSArICMgU2V0IGZpbGwgY29sb3JzCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgZ2d0aXRsZSgnWm9vbWluZyBpbiBvbiByaWdodCBoYW5kIHNpZGUgKGV4dHJlbWVseSBjb25zaXN0ZW50bHkgZG93bnJlZ3VsYXRlZCBwYXRod2F5cyknKQoKZ2dzYXZlKCJkZXNlcS9vdXRwdXQvY29uc2lzdGVudGx5X2Rvd25yZWd1bGF0ZWRfcGF0aHdheXNfYWxsX2NlbGxsaW5lcy5zdmciLAogICAgICAgcGxvdCA9IGxhc3RfcGxvdCgpLAogICAgICAgZGV2aWNlID0gInN2ZyIsCiAgICAgICB3aWR0aCA9IDEwLAogICAgICAgaGVpZ2h0ID0gNSwKICAgICAgIHVuaXRzID0gImluIikKYGBgCiMjIyBQbG90IGRpZmZlcmVudGlhbCBnZW5lcwoKUGxvdCBzaG93cyBob3cgdGhlIHNwcmVhZCBvZiBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbCBnZW5lcyBhY3Jvc3Mgc2Vuc2l0aXZlL3Jlc2lzdGFudCBwYWlycy4KCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDV9CiMgZm9ybWF0dGVkX2dlbmVfcmVzdWx0cyA9IGZ1bGxfcmVzdWx0c19nZW5lc1ssYygibnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQiLCAibnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCIpXQoKcGxvdF9kYXRhID0gZnVsbF9yZXN1bHRzX2dlbmVzWyxjKCJudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCIsICJudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkIildCgojIHBsb3RfZGF0YSA8LSBmdWxsX3Jlc3VsdHNfZ2VuZXMgJT4lCiMgICBncm91cF9ieShudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCwgbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCkgJT4lCiMgICBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgojIE9yZGVycyBwcmltYXJpbHkgYnkgaG93IG1hbnkgbW9yZSBwYWlycyB3ZXJlIHNpZyB1cCB0aGFuIHNpZyBkb3duIHJlZ3VsYXRlZAojIE9yZGVycyBzZWNvbmRhcmlseSBieSBob3cgZmV3IHBhaXJzIHdlcmUgcmVndWxhdGVkIGluIHRoZSBub24tZG9taW5hbnQgZGlyZWN0aW9uCiMgVGhpcyBvcmRlcmluZyBkb2VzIHRoZSBmb2xsb3dpbmc6ICg3LCAwKSA+ICg2LCAwKSA+ICg1LCAwKSA+ICg2LCAxKSA+ICg3LCAyKQpwbG90X2RhdGEgPC0gcGxvdF9kYXRhICU+JQogIG11dGF0ZShvcmRlciA9IGlmZWxzZShudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCA+IG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQsCiAgICAgICAgICAgICAgICAgICAgICAgIG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkIC0gbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCAqIDEuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgIC0xICogKG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQgLSBudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCAqIDEuMDEpKSkKCnBsb3RfZGF0YSA8LSBwbG90X2RhdGEgJT4lCiAgYXJyYW5nZShkZXNjKG9yZGVyKSkKCnBsb3RfZGF0YSA9IHBsb3RfZGF0YSAlPiUKICBtdXRhdGUocGFuZWwgPSBpZmVsc2UobnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQgPiBudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkLAogICAgICAgICAgICAgICAgICAgICAgICAib3ZlcmFsbCB1cHJlZ3VsYXRlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkID09IG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImV2ZW5seSB1cCBhbmQgZG93biByZWd1bGF0ZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm92ZXJhbGwgZG93bnJlZ3VsYXRlZCIpKSkKcGxvdF9kYXRhJHBhbmVsID0gZmFjdG9yKHBsb3RfZGF0YSRwYW5lbCwgbGV2ZWxzID0gYygib3ZlcmFsbCB1cHJlZ3VsYXRlZCIsICJldmVubHkgdXAgYW5kIGRvd24gcmVndWxhdGVkIiwgIm92ZXJhbGwgZG93bnJlZ3VsYXRlZCIpKQoKIyBBZGQgYSBzbWFsbCBhbW91bnQgb2YgY29sb3IgaWYgbm90IHJlZ3VsYXRlZAp1bnJlZ3VsYXRlZCA9IHBsb3RfZGF0YSRudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCA9PSAwICYgcGxvdF9kYXRhJG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQgPT0gMApwbG90X2RhdGFbdW5yZWd1bGF0ZWQsXSRudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCA9IDAuMDA1CnBsb3RfZGF0YVt1bnJlZ3VsYXRlZCxdJG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQgPSAtMC4wMDUKCiMgb3JnYW5pemUgdGhlIHggY29vcmRpbmF0ZXMgYmFzZWQgb24gcGFuZWwKcGxvdF9kYXRhJGxlZnRfcGxhY2VtZW50ID0gMDoobnJvdyhwbG90X2RhdGEpIC0gMSkKZXF1YWxfcGFuZWxfc3RhcnQgPSBtaW4ocGxvdF9kYXRhW3Bsb3RfZGF0YSRwYW5lbCA9PSAiZXZlbmx5IHVwIGFuZCBkb3duIHJlZ3VsYXRlZCIsICJsZWZ0X3BsYWNlbWVudCJdKQpwbG90X2RhdGFbcGxvdF9kYXRhJHBhbmVsID09ICJldmVubHkgdXAgYW5kIGRvd24gcmVndWxhdGVkIixdJGxlZnRfcGxhY2VtZW50ID0gcGxvdF9kYXRhW3Bsb3RfZGF0YSRwYW5lbCA9PSAiZXZlbmx5IHVwIGFuZCBkb3duIHJlZ3VsYXRlZCIsXSRsZWZ0X3BsYWNlbWVudCAtIGVxdWFsX3BhbmVsX3N0YXJ0CmRvd25fcGFuZWxfc3RhcnQgPSBtaW4ocGxvdF9kYXRhW3Bsb3RfZGF0YSRwYW5lbCA9PSAib3ZlcmFsbCBkb3ducmVndWxhdGVkIiwgImxlZnRfcGxhY2VtZW50Il0pCnBsb3RfZGF0YVtwbG90X2RhdGEkcGFuZWwgPT0gIm92ZXJhbGwgZG93bnJlZ3VsYXRlZCIsXSRsZWZ0X3BsYWNlbWVudCA9IHBsb3RfZGF0YVtwbG90X2RhdGEkcGFuZWwgPT0gIm92ZXJhbGwgZG93bnJlZ3VsYXRlZCIsXSRsZWZ0X3BsYWNlbWVudCAtIGRvd25fcGFuZWxfc3RhcnQKCnBsb3RfZGF0YSRjZW50ZXJfcGxhY2VtZW50ID0gcGxvdF9kYXRhJGxlZnRfcGxhY2VtZW50ICsgMC41CgojIG5leHRfcGxhY2VtZW50ID0gMAojIGZvcihyb3cgaW4gMTpucm93KHBsb3RfZGF0YSkpIHsKIyAgIG5ld19wbGFjZW1lbnQgPSBuZXh0X3BsYWNlbWVudAojICAgcGxvdF9kYXRhW3JvdywgImxlZnRfcGxhY2VtZW50Il0gPSBuZXdfcGxhY2VtZW50CiMgICBuZXh0X3BsYWNlbWVudCA9IG5ld19wbGFjZW1lbnQgKyBwbG90X2RhdGFbcm93LCAiY291bnQiXQojIH0KCiMgTmVlZCB0aGUgY2VudGVyIG9mIHRoZSBiYXIgZm9yIHBsb3R0aW5nIHB1cnBvc2VzCiMgcGxvdF9kYXRhJGNlbnRlcl9wbGFjZW1lbnQgPSBwbG90X2RhdGEkbGVmdF9wbGFjZW1lbnQgKyBwbG90X2RhdGEkY291bnQvMgoKcHJpbnQocGxvdF9kYXRhKQoKIyBDcmVhdGUgYmFyIHBsb3QKZ2dwbG90KHBsb3RfZGF0YSkgKwogIGZhY2V0X3dyYXAofnBhbmVsLCBuY29sID0gMywgc2NhbGVzID0gImZpeGVkIikgKwogIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IC0xICogbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwgZmlsbCA9ICJkb3ducmVndWxhdGlvbiIsIHdpZHRoID0gMSksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQsIGZpbGwgPSAidXByZWd1bGF0aW9uIiwgd2lkdGggPSAxKSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICAjIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IC0xICogbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwgZmlsbCA9ICJkb3ducmVndWxhdGlvbiIsIHdpZHRoID0gY291bnQpLCBzdGF0ID0gImlkZW50aXR5IikgKwogICMgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQsIGZpbGwgPSAidXByZWd1bGF0aW9uIiwgd2lkdGggPSBjb3VudCksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLW1heChwbG90X2RhdGEkbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCkgLTEsIG1heChwbG90X2RhdGEkbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQpKSArMSkgKwogICMgU2V0IHktYXhpcyBsaW1pdHMKICAjIGNvb3JkX2ZsaXAoKSArICAjIEZsaXAgdGhlIGNvb3JkaW5hdGVzIHRvIGNyZWF0ZSBhIHNpZGV3YXlzIHBsb3QKICB0aGVtZV9taW5pbWFsKCkgKyAgIyBBcHBseSBhIG1pbmltYWwgdGhlbWUKICBsYWJzKHggPSAiR2VuZXMiLCB5ID0gIiMgcmVzaXN0YW50IGNlbGxsaW5lcyBzaG93aW5nIHNpZ25pZmljYW50IHJlZ3VsYXRpb24gb2YgZ2VuZSIpICsgIyBTZXQgYXhpcyBsYWJlbHMKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJ1cHJlZ3VsYXRpb24iID0gImdyZWVuIiwgImRvd25yZWd1bGF0aW9uIiA9ICJyZWQiKSkgKyAjIFNldCBmaWxsIGNvbG9ycwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgICAjIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgIyBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGdndGl0bGUoJ0dlbmUgcmVndWxhdGlvbiBpbiByZXNpc3RhbnQgY2VsbGxpbmVzIChhcyBjb21wYXJlZCB0byBjb3JyZXNwb25kaW5nIHNlbnNpdGl2ZSBjZWxsbGluZSknKQogIAoKZ2dzYXZlKCJkZXNlcS9vdXRwdXQvZGlmZmVyZW50aWFsX2dlbmVzX2FsbF9jZWxsbGluZXMuc3ZnIiwKICAgICAgIHBsb3QgPSBsYXN0X3Bsb3QoKSwKICAgICAgIGRldmljZSA9ICJzdmciLAogICAgICAgd2lkdGggPSAxMCwKICAgICAgIGhlaWdodCA9IDUsCiAgICAgICB1bml0cyA9ICJpbiIpCmBgYAoKQ2xvc2UgdXAgb2YgY29uc2lzdGVudGx5IHVwcmVndWxhdGVkIGdlbmVzCgpgYGB7ciwgZmlndXJlLndpZHRoID0gMTAsIGZpZ3VyZS5oZWlnaHQgPSA1fQojIEVuZCB0aGUgcGxvdCBiZXR3ZWVuICJjYXRlZ29yaWVzIiBvZiBnZW5lcywgaW5jbHVkaW5nIGxlc3MgdGhhbiAxMDAgZ2VuZXMgdG90YWwKZW5kX2luZGV4ID0gMQpmb3IoaSBpbiAxOjEwMCkgewogIGlmIChwbG90X2RhdGEkb3JkZXJbaV0gIT0gcGxvdF9kYXRhJG9yZGVyW2krMV0pIHsKICAgIGVuZF9pbmRleCA9IGkKICB9Cn0KCnBsb3RfZGF0YV91cCA9IHBsb3RfZGF0YVsxOmVuZF9pbmRleCxdCnBsb3RfZGF0YV91cCRnZW5lID0gcm93bmFtZXMocGxvdF9kYXRhX3VwKQoKcHJpbnQocGxvdF9kYXRhX3VwKQoKZ2dwbG90KHBsb3RfZGF0YV91cCkgKwogIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IC0xICogbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwgZmlsbCA9ICJkb3ducmVndWxhdGlvbiIsIHdpZHRoID0gMSksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQsIGZpbGwgPSAidXByZWd1bGF0aW9uIiwgd2lkdGggPSAxKSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gMSwgbGFiZWwgPSBnZW5lKSwgc2l6ZSA9IDIsIGNvbG9yID0gImJsYWNrIiwgYW5nbGUgPSA5MCkgKwogICMgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gLTEgKiBudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkLCBmaWxsID0gImRvd25yZWd1bGF0aW9uIiwgd2lkdGggPSBjb3VudCksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgIyBnZW9tX2JhcihhZXMoeCA9IGNlbnRlcl9wbGFjZW1lbnQsIHkgPSBudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCwgZmlsbCA9ICJ1cHJlZ3VsYXRpb24iLCB3aWR0aCA9IGNvdW50KSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygtbWF4KHBsb3RfZGF0YV91cCRudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkKSAtMSwgbWF4KHBsb3RfZGF0YV91cCRudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCkpICsxKSArCiAgIyBTZXQgeS1heGlzIGxpbWl0cwogICMgY29vcmRfZmxpcCgpICsgICMgRmxpcCB0aGUgY29vcmRpbmF0ZXMgdG8gY3JlYXRlIGEgc2lkZXdheXMgcGxvdAogIHRoZW1lX21pbmltYWwoKSArICAjIEFwcGx5IGEgbWluaW1hbCB0aGVtZQogIGxhYnMoeCA9ICJHZW5lcyIsIHkgPSAiIyByZXNpc3RhbnQgY2VsbGxpbmVzIHNob3dpbmcgc2lnbmlmaWNhbnQgcmVndWxhdGlvbiBvZiBnZW5lIikgKyAjIFNldCBheGlzIGxhYmVscwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInVwcmVndWxhdGlvbiIgPSAiZ3JlZW4iLCAiZG93bnJlZ3VsYXRpb24iID0gInJlZCIpKSArICMgU2V0IGZpbGwgY29sb3JzCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgICMgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAjIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgZ2d0aXRsZSgnWm9vbWluZyBpbiBvbiBsZWZ0IGhhbmQgc2lkZSAoZXh0cmVtZWx5IGNvbnNpc3RlbnRseSB1cHJlZ3VsYXRlZCBnZW5lcyknKQogICMgZ2d0aXRsZSgnR2VuZXMgZXh0cmVtZWx5IGNvbnNpc3RlbnRseSB1cHJlZ3VsYXRlZCBpbiByZXNpc3RhbnQgY2VsbGxpbmVzJykKCmdnc2F2ZSgiZGVzZXEvb3V0cHV0L2NvbnNpc3RlbnRseV91cHJlZ3VsYXRlZF9nZW5lc19hbGxfY2VsbGxpbmVzLnN2ZyIsCiAgICAgICBwbG90ID0gbGFzdF9wbG90KCksCiAgICAgICBkZXZpY2UgPSAic3ZnIiwKICAgICAgIHdpZHRoID0gMTAsCiAgICAgICBoZWlnaHQgPSA1LAogICAgICAgdW5pdHMgPSAiaW4iKQpgYGAKCkNsb3NlIHVwIG9mIGNvbnNpc3RlbnRseSBkb3ducmVndWxhdGVkIGdlbmVzCgpgYGB7ciwgZmlndXJlLndpZHRoID0gMTAsIGZpZ3VyZS5oZWlnaHQgPSA1fQojIEVuZCB0aGUgcGxvdCBiZXR3ZWVuICJjYXRlZ29yaWVzIiBvZiBnZW5lcywgaW5jbHVkaW5nIGxlc3MgdGhhbiAxMDAgZ2VuZXMgdG90YWwKc3RhcnRfaW5kZXggPSAxCmZvcihpIGluIG5yb3cocGxvdF9kYXRhKToobnJvdyhwbG90X2RhdGEpIC0gOTkpKSB7CiAgaWYgKHBsb3RfZGF0YSRvcmRlcltpXSAhPSBwbG90X2RhdGEkb3JkZXJbaS0xXSkgewogICAgc3RhcnRfaW5kZXggPSBpCiAgfQp9CgpwbG90X2RhdGFfZG93biA9IHBsb3RfZGF0YVtzdGFydF9pbmRleDpucm93KHBsb3RfZGF0YSksXQpwbG90X2RhdGFfZG93biRjZW50ZXJfcGxhY2VtZW50ID0gcGxvdF9kYXRhX2Rvd24kY2VudGVyX3BsYWNlbWVudCAtIHN0YXJ0X2luZGV4ICsgMSAjIFN0YXJ0IGF0IDAKcGxvdF9kYXRhX2Rvd24kZ2VuZSA9IHJvd25hbWVzKHBsb3RfZGF0YV9kb3duKQoKcHJpbnQocGxvdF9kYXRhX2Rvd24pCgpnZ3Bsb3QocGxvdF9kYXRhX2Rvd24pICsKICBnZW9tX2JhcihhZXMoeCA9IGNlbnRlcl9wbGFjZW1lbnQsIHkgPSAtMSAqIG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQsIGZpbGwgPSAiZG93bnJlZ3VsYXRpb24iLCB3aWR0aCA9IDEpLCBzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkLCBmaWxsID0gInVwcmVndWxhdGlvbiIsIHdpZHRoID0gMSksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV90ZXh0KGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IDIsIGxhYmVsID0gZ2VuZSksIHNpemUgPSAyLCBjb2xvciA9ICJibGFjayIsIGFuZ2xlID0gOTApICsKICAjIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IC0xICogbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwgZmlsbCA9ICJkb3ducmVndWxhdGlvbiIsIHdpZHRoID0gY291bnQpLCBzdGF0ID0gImlkZW50aXR5IikgKwogICMgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQsIGZpbGwgPSAidXByZWd1bGF0aW9uIiwgd2lkdGggPSBjb3VudCksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLW1heChwbG90X2RhdGFfZG93biRudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkKSAtMSwgbWF4KHBsb3RfZGF0YV9kb3duJG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkKSkgKzEpICsKICAjIFNldCB5LWF4aXMgbGltaXRzCiAgIyBjb29yZF9mbGlwKCkgKyAgIyBGbGlwIHRoZSBjb29yZGluYXRlcyB0byBjcmVhdGUgYSBzaWRld2F5cyBwbG90CiAgdGhlbWVfbWluaW1hbCgpICsgICMgQXBwbHkgYSBtaW5pbWFsIHRoZW1lCiAgbGFicyh4ID0gIkdlbmVzIiwgeSA9ICIjIHJlc2lzdGFudCBjZWxsbGluZXMgc2hvd2luZyBzaWduaWZpY2FudCByZWd1bGF0aW9uIG9mIGdlbmUiKSArICMgU2V0IGF4aXMgbGFiZWxzCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygidXByZWd1bGF0aW9uIiA9ICJncmVlbiIsICJkb3ducmVndWxhdGlvbiIgPSAicmVkIikpICsgIyBTZXQgZmlsbCBjb2xvcnMKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuZ3JpZC5taW5vci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICAgICAgIyBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICMgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsKICBnZ3RpdGxlKCdab29taW5nIGluIG9uIHJpZ2h0IGhhbmQgc2lkZSAoZXh0cmVtZWx5IGNvbnNpc3RlbnRseSBkb3ducmVndWxhdGVkIGdlbmVzKScpCiAgIyBnZ3RpdGxlKCdHZW5lcyBjb25zaXN0ZW50bHkgdXByZWd1bGF0ZWQgaW4gcmVzaXN0YW50IGNlbGxsaW5lcycpCgpnZ3NhdmUoImRlc2VxL291dHB1dC9jb25zaXN0ZW50bHlfZG93bnJlZ3VsYXRlZF9nZW5lc19hbGxfY2VsbGxpbmVzLnN2ZyIsCiAgICAgICBwbG90ID0gbGFzdF9wbG90KCksCiAgICAgICBkZXZpY2UgPSAic3ZnIiwKICAgICAgIHdpZHRoID0gMTAsCiAgICAgICBoZWlnaHQgPSA1LAogICAgICAgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIFVwc2V0IHBsb3RzCgpVcHNldCBwbG90cyBhcmUgYWtpbiB0byBhIFZlbm4gZGlhZ3JhbSBhbmQgc2hvdyB0aGUgb3ZlcmxhcCBvZiBnZW5lcyB0aGF0IHdlcmUgc2lnbmlmaWNhbnRseSB1cCBhbmQgZG93biByZWd1bGF0ZWQgYnkgZWFjaCByZXNpc3RhbnQgY2VsbGxpbmUgKGFzIGNvbXBhcmVkIHRvIGl0cyByZXNwZWN0aXZlIGNvbnRyb2wpLgoKTm90ZSB0aGF0IHRoZXNlIHVwc2V0IHBsb3RzIHVzZSB0aGUgImRpc3RpbmN0IiBtb2RlIChtZWFuaW5nIHRoYXQgZWFjaCByZXByZXNlbnRlZCBpbnRlcnNlY3Rpb24gY29uc2lzdHMgb2YgdGhlICJpbnRlcnNlY3Rpb24gZWxlbWVudHMgdGhhdCBiZWxvbmcgdG8gdGhlIHNldHMgZGVmaW5pbmcgdGhlIGludGVyc2VjdGlvbiBidXQgbm90IHRvIGFueSBvdGhlciBzZXQiKS4KClRPRE86IHdoaWNoIG9mIHRoZSBodW5kcmVkcyBvZiBwb3NzaWJsZSBjb21iaW5hdGlvbnMgdG8gc2hvdz8gRm9yIG5vdyBJJ20gc2VwYXJhdGluZyB1cHJlZ3VsYXRpb24gYW5kIGRvd25yZWd1bGF0aW9uIHRvIGRlY3JlYXNlIHRoZSBudW1iZXIgb2YgcG9zc2libGUgaW50ZXJzZWN0aW9ucyBieSBhIGxvdCwgYnV0IEknbSBzdGlsbCBvbmx5IHNob3dpbmcgdGhlIHRvcCA1MCBpbnRlcnNlY3Rpb25zIChyYW5rZWQgYnkgZGVncmVlIC0tIGkuZS4gbnVtYmVyIG9mIGNlbGxMaW5lcyB0aGF0IHNoYXJlIHRoYXQgdXAgb3IgZG93bnJlZ3VsYXRlZCBnZW5lKS4KCiMjIyBDb21iaW5lZCB1cCBhbmQgZG93biBnZW5lIHVwc2V0IHBsb3QKCmBgYHtyIGNvbWJpbmVkIHVwc2V0IHBsb3R9CmNvbWJpbmVkVXBzZXRMaXN0SW5wdXQgPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpKQpmb3IgKHBhaXIgaW4gc2Vuc2l0aXZlX3Jlc2lzdGFudF9wYWlycykgewogIHNwbGl0IDwtIHN0cnNwbGl0KHBhaXIsICJfdnNfIikKICBjb250IDwtIHNwbGl0W1sxXV1bMl0KICBleHAgPC0gc3BsaXRbWzFdXVsxXQogIGZvciAoZGlyIGluIGMoInVwIiwgImRvd24iKSkgewogICAgcmVndWxhdGVkR2VuZXNGaWxlID0gc3RyX2ludGVycCgiZGVzZXEvb3V0cHV0LyR7ZXhwfV92c18ke2NvbnR9X3NpZ25pZmljYW50bHlfJHtkaXJ9cmVndWxhdGVkX2dlbmVzLmNzdiIpCiAgICByZWd1bGF0ZWRHZW5lcyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KHJlZ3VsYXRlZEdlbmVzRmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICAgIGtleSA9IHN0cl9pbnRlcnAoIiR7ZXhwfV9zaWdfJHtkaXJ9cmVnX2dlbmVzIikKICAgIGNvbWJpbmVkVXBzZXRMaXN0SW5wdXRbW2tleV1dID0gcm93bmFtZXMocmVndWxhdGVkR2VuZXMpCiAgfQp9Cgp1cHNldChmcm9tTGlzdChjb21iaW5lZFVwc2V0TGlzdElucHV0KSwgbnNldHMgPSAxNCwgbmludGVyc2VjdHMgPSA1MCwgb3JkZXIuYnkgPSAiZGVncmVlIikKYGBgCgojIyMgVXByZWd1bGF0ZWQgZ2VuZSB1cHNldCBwbG90CgpTaG93cyBvbmx5IHRoZSB1cHJlZ3VsYXRlZCBnZW5lcy4KCmBgYHtyIHVwcmVnIHVwc2V0IHBsb3R9CnVwcmVnVXBzZXRMaXN0SW5wdXQgPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpKQpmb3IgKHBhaXIgaW4gc2Vuc2l0aXZlX3Jlc2lzdGFudF9wYWlycykgewogIHNwbGl0IDwtIHN0cnNwbGl0KHBhaXIsICJfdnNfIikKICBjb250IDwtIHNwbGl0W1sxXV1bMl0KICBleHAgPC0gc3BsaXRbWzFdXVsxXQogIAogIHVwR2VuZXNGaWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke2V4cH1fdnNfJHtjb250fV9zaWduaWZpY2FudGx5X3VwcmVndWxhdGVkX2dlbmVzLmNzdiIpCiAgdXBHZW5lcyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KHVwR2VuZXNGaWxlLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpKQogIGtleSA9IHN0cl9pbnRlcnAoIiR7ZXhwfV9zaWdfdXByZWdfZ2VuZXMiKQogIHVwcmVnVXBzZXRMaXN0SW5wdXRbW2tleV1dID0gcm93bmFtZXModXBHZW5lcykKfQoKdXBzZXQoZnJvbUxpc3QodXByZWdVcHNldExpc3RJbnB1dCksIG5zZXRzID0gNywgbmludGVyc2VjdHMgPSA1MCwgb3JkZXIuYnkgPSAiZGVncmVlIikKYGBgCgpPdXRwdXQgdGhlIGxpc3RzIG9mIGdlbmVzIGNvcnJlc3BvbmRpbmcgdG8gZWFjaCBjb2x1bW4gb2YgdGhlIHVwc2V0IHBsb3QKCmBgYHtyIHVwcmVndWxhdGVkIHVwc2V0IHBsb3QgZGF0YX0KZnJvbUxpc3QodXByZWdVcHNldExpc3RJbnB1dCkKdXByZWdVcHNldFBsb3RHZW5lcy5kZiA8LSBnZXRVcHNldFBsb3REYXRhKHVwcmVnVXBzZXRMaXN0SW5wdXQpCndyaXRlLmNzdih1cHJlZ1Vwc2V0UGxvdEdlbmVzLmRmLCBmaWxlID0gImRlc2VxL291dHB1dC91cHNldF9wbG90X2xpc3RzX3NpZ25pZmljYW50bHlfdXByZWd1bGF0ZWRfZ2VuZXMuY3N2IikKdXByZWdVcHNldFBsb3RHZW5lcy5kZgpgYGAKCiMjIyBEb3ducmVndWxhdGVkIGdlbmUgdXBzZXQgcGxvdAoKU2hvd3Mgb25seSB0aGUgZG93bnJlZ3VsYXRlZCBnZW5lcy4KCmBgYHtyIGRvd25yZWcgdXBzZXQgcGxvdH0KZG93bnJlZ1Vwc2V0TGlzdElucHV0IDwtIHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoPWxlbmd0aChzZW5zaXRpdmVfcmVzaXN0YW50X3BhaXJzKSkKCmZpcnN0X3BhaXIgPSBUUlVFCmNvbnNpc3RlbnRfZG93bl9nZW5lcyA9IGMoKQoKZm9yIChwYWlyIGluIHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpIHsKICBzcGxpdCA8LSBzdHJzcGxpdChwYWlyLCAiX3ZzXyIpCiAgZXhwIDwtIHNwbGl0W1sxXV1bMV0KICAKICBkb3duR2VuZXNGaWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfZG93bnJlZ3VsYXRlZF9nZW5lcy5jc3YiKQogIGRvd25HZW5lcyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KGRvd25HZW5lc0ZpbGUsIHNlcCA9ICIsIiwgaGVhZGVyID0gVFJVRSwgcm93Lm5hbWVzID0gMSkpCiAga2V5ID0gc3RyX2ludGVycCgiJHtleHB9X3NpZ19kb3ducmVnX2dlbmVzIikKICBkb3ducmVnVXBzZXRMaXN0SW5wdXRbW2tleV1dID0gcm93bmFtZXMoZG93bkdlbmVzKQogIAogIGlmIChmaXJzdF9wYWlyID09IFRSVUUpIHsKICAgIGNvbnNpc3RlbnRfZG93bl9nZW5lcyA9IHJvd25hbWVzKGRvd25HZW5lcykKICAgIGZpcnN0X3BhaXIgPSBGQUxTRQogIH0gZWxzZSB7CiAgICBjb25zaXN0ZW50X2Rvd25fZ2VuZXMgPSBpbnRlcnNlY3QoY29uc2lzdGVudF9kb3duX2dlbmVzLCByb3duYW1lcyhkb3duR2VuZXMpKQogIH0KfQoKdXBzZXQoZnJvbUxpc3QoZG93bnJlZ1Vwc2V0TGlzdElucHV0KSwgbnNldHMgPSA3LCBuaW50ZXJzZWN0cyA9IDUwLCBvcmRlci5ieSA9ICJkZWdyZWUiKQoKcHJpbnQoIkdlbmVzIGRvd25yZWd1bGF0ZWQgaW4gYWxsIHNlbnNpdGl2ZS9yZXNpc3RhbnQgcGFpcnMiKQpwcmludChjb25zaXN0ZW50X2Rvd25fZ2VuZXMpCmBgYAoKT3V0cHV0IHRoZSBsaXN0cyBvZiBnZW5lcyBjb3JyZXNwb25kaW5nIHRvIGVhY2ggY29sdW1uIG9mIHRoZSB1cHNldCBwbG90CgpgYGB7ciBkb3ducmVndWxhdGVkIHVwc2V0IHBsb3QgZGF0YSBpc29saW5lc30KZG93bnJlZ1Vwc2V0UGxvdEdlbmVzLmRmIDwtIGdldFVwc2V0UGxvdERhdGEoZG93bnJlZ1Vwc2V0TGlzdElucHV0KQp3cml0ZS5jc3YoZG93bnJlZ1Vwc2V0UGxvdEdlbmVzLmRmLCBmaWxlID0gImRlc2VxL291dHB1dC91cHNldF9wbG90X2lzb2xpbmVzX2xpc3RzX3NpZ25pZmljYW50bHlfZG93bnJlZ3VsYXRlZF9nZW5lcy5jc3YiKQpkb3ducmVnVXBzZXRQbG90R2VuZXMuZGYKYGBgCgoKCiMjIyBVcHNldCBwbG90cyBhY3Jvc3MgaXNvbGluZXMKCkdlbmUgaW5jbHVkZWQgaW4gaXNvbGluZSBpZiBpdCBpcyBzaWcgdXByZWd1bGF0ZWQgaW4gQU5ZIG9mIHRoZSBjZWxsIGxpbmVzCgpgYGB7ciB1cHJlZyB1cHNldCBieSBpc29saW5lIGFueX0KdXBzZXRMaXN0SW5wdXQgPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKGlzb2xpbmVzKSkKCmZvciAoaXNvbGluZSBpbiB1bmlxdWUoaXNvbGluZXMkaXNvbGluZSkpIHsKICBwYWlycyA9IGlzb2xpbmVzW2lzb2xpbmVzJGlzb2xpbmUgPT0gaXNvbGluZSwgInBhaXIiXQogIHVwR2VuZXMgPC0gbGlzdCgpCiAgCiAgZm9yIChwYWlyIGluIHBhaXJzKSB7CiAgICB1cEdlbmVzRmlsZSA9IHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfdXByZWd1bGF0ZWRfZ2VuZXMuY3N2IikKICAgIG5ld1VwR2VuZXMgPC0gYXMuZGF0YS5mcmFtZShyZWFkLmNzdih1cEdlbmVzRmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICAgIHVwR2VuZXMgPC0gdW5pcXVlKGModXBHZW5lcywgcm93bmFtZXMobmV3VXBHZW5lcykpKQogIH0KICAKICB1cHNldExpc3RJbnB1dFtbaXNvbGluZV1dID0gdXBHZW5lcwp9Cgp1cHNldChmcm9tTGlzdCh1cHNldExpc3RJbnB1dCksIG5zZXRzID0gNywgbmludGVyc2VjdHMgPSA1MCwgb3JkZXIuYnkgPSAiZGVncmVlIikKCiMgT3V0cHV0IHRoZSBsaXN0cyBvZiBnZW5lcyBjb3JyZXNwb25kaW5nIHRvIGVhY2ggY29sdW1uIG9mIHRoZSB1cHNldCBwbG90CnVwcmVnVXBzZXRQbG90R2VuZXMuZGYgPC0gZ2V0VXBzZXRQbG90RGF0YSh1cHNldExpc3RJbnB1dCkKd3JpdGUuY3N2KHVwcmVnVXBzZXRQbG90R2VuZXMuZGYsIGZpbGUgPSAiZGVzZXEvb3V0cHV0L3Vwc2V0X3Bsb3RfaXNvbGluZXNfbGlzdHNfc2lnbmlmaWNhbnRseV91cHJlZ3VsYXRlZF9nZW5lc19hbnlfc3VibGluZXMuY3N2IikKdXByZWdVcHNldFBsb3RHZW5lcy5kZgpgYGAKCkdlbmUgaW5jbHVkZWQgaW4gaXNvbGluZSBpZiBpdCBpcyBzaWcgdXByZWd1bGF0ZWQgaW4gQUxMIG9mIHRoZSBjZWxsIGxpbmVzCgpgYGB7ciB1cHJlZyB1cHNldCBieSBpc29saW5lIGFsbH0KdXBzZXRMaXN0SW5wdXQgPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKGlzb2xpbmVzKSkKCmZvciAoaXNvbGluZSBpbiB1bmlxdWUoaXNvbGluZXMkaXNvbGluZSkpIHsKICBwYWlycyA9IGlzb2xpbmVzW2lzb2xpbmVzJGlzb2xpbmUgPT0gaXNvbGluZSwgInBhaXIiXQogIAogIHBhaXIgPSBwYWlyc1sxXQogIHVwR2VuZXNGaWxlID0gc3RyX2ludGVycCgiZGVzZXEvb3V0cHV0LyR7cGFpcn1fc2lnbmlmaWNhbnRseV91cHJlZ3VsYXRlZF9nZW5lcy5jc3YiKQogIHVwR2VuZXMgPC0gYXMuZGF0YS5mcmFtZShyZWFkLmNzdih1cEdlbmVzRmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICB1cEdlbmVzIDwtIHJvd25hbWVzKHVwR2VuZXMpCiAgCiAgaWYgKGxlbmd0aChwYWlycykgPiAxKSB7CiAgICBmb3IgKHBhaXIgaW4gcGFpcnNbMjpsZW5ndGgocGFpcnMpXSkgewogICAgICB1cEdlbmVzRmlsZSA9IHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfdXByZWd1bGF0ZWRfZ2VuZXMuY3N2IikKICAgICAgbmV3VXBHZW5lcyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KHVwR2VuZXNGaWxlLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpKQogICAgICB1cEdlbmVzIDwtIGludGVyc2VjdCh1cEdlbmVzLCByb3duYW1lcyhuZXdVcEdlbmVzKSkKICAgIH0KICB9CiAgCiAgdXBzZXRMaXN0SW5wdXRbW2lzb2xpbmVdXSA9IHVwR2VuZXMKfQoKdXBzZXQoZnJvbUxpc3QodXBzZXRMaXN0SW5wdXQpLCBuc2V0cyA9IDcsIG5pbnRlcnNlY3RzID0gNTAsIG9yZGVyLmJ5ID0gImRlZ3JlZSIpCgojIE91dHB1dCB0aGUgbGlzdHMgb2YgZ2VuZXMgY29ycmVzcG9uZGluZyB0byBlYWNoIGNvbHVtbiBvZiB0aGUgdXBzZXQgcGxvdAp1cHJlZ1Vwc2V0UGxvdEdlbmVzLmRmIDwtIGdldFVwc2V0UGxvdERhdGEodXBzZXRMaXN0SW5wdXQpCndyaXRlLmNzdih1cHJlZ1Vwc2V0UGxvdEdlbmVzLmRmLCBmaWxlID0gImRlc2VxL291dHB1dC91cHNldF9wbG90X2lzb2xpbmVzX2xpc3RzX3NpZ25pZmljYW50bHlfdXByZWd1bGF0ZWRfZ2VuZXNfYWxsX3N1YmxpbmVzLmNzdiIpCnVwcmVnVXBzZXRQbG90R2VuZXMuZGYKYGBgCgpHZW5lIGluY2x1ZGVkIGluIGlzb2xpbmUgaWYgaXQgaXMgc2lnIGRvd25yZWd1bGF0ZWQgaW4gQU5ZIG9mIHRoZSBjZWxsIGxpbmVzCgpgYGB7ciBkb3ducmVnIHVwc2V0IGJ5IGlzb2xpbmUgYW55fQp1cHNldExpc3RJbnB1dCA8LSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aD1sZW5ndGgoaXNvbGluZXMpKQoKZm9yIChpc29saW5lIGluIHVuaXF1ZShpc29saW5lcyRpc29saW5lKSkgewogIHBhaXJzID0gaXNvbGluZXNbaXNvbGluZXMkaXNvbGluZSA9PSBpc29saW5lLCAicGFpciJdCiAgZG93bkdlbmVzIDwtIGxpc3QoKQogIAogIGZvciAocGFpciBpbiBwYWlycykgewogICAgZG93bkdlbmVzRmlsZSA9IHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfZG93bnJlZ3VsYXRlZF9nZW5lcy5jc3YiKQogICAgbmV3RG93bkdlbmVzIDwtIGFzLmRhdGEuZnJhbWUocmVhZC5jc3YoZG93bkdlbmVzRmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICAgIGRvd25HZW5lcyA8LSB1bmlxdWUoYyhkb3duR2VuZXMsIHJvd25hbWVzKG5ld0Rvd25HZW5lcykpKQogIH0KICAKICB1cHNldExpc3RJbnB1dFtbaXNvbGluZV1dID0gZG93bkdlbmVzCn0KCnVwc2V0KGZyb21MaXN0KHVwc2V0TGlzdElucHV0KSwgbnNldHMgPSA3LCBuaW50ZXJzZWN0cyA9IDUwLCBvcmRlci5ieSA9ICJkZWdyZWUiKQoKIyBPdXRwdXQgdGhlIGxpc3RzIG9mIGdlbmVzIGNvcnJlc3BvbmRpbmcgdG8gZWFjaCBjb2x1bW4gb2YgdGhlIHVwc2V0IHBsb3QKZG93bnJlZ1Vwc2V0UGxvdEdlbmVzLmRmIDwtIGdldFVwc2V0UGxvdERhdGEodXBzZXRMaXN0SW5wdXQpCndyaXRlLmNzdihkb3ducmVnVXBzZXRQbG90R2VuZXMuZGYsIGZpbGUgPSAiZGVzZXEvb3V0cHV0L3Vwc2V0X3Bsb3RfaXNvbGluZXNfbGlzdHNfc2lnbmlmaWNhbnRseV9kb3ducmVndWxhdGVkX2dlbmVzX2FueV9zdWJsaW5lcy5jc3YiKQpkb3ducmVnVXBzZXRQbG90R2VuZXMuZGYKYGBgCgpHZW5lIGluY2x1ZGVkIGluIGlzb2xpbmUgaWYgaXQgaXMgc2lnIGRvd25yZWd1bGF0ZWQgaW4gQUxMIG9mIHRoZSBjZWxsIGxpbmVzCgpgYGB7ciBkb3ducmVnIHVwc2V0IGJ5IGlzb2xpbmUgYWxsfQp1cHNldExpc3RJbnB1dCA8LSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aD1sZW5ndGgoaXNvbGluZXMpKQoKZm9yIChpc29saW5lIGluIHVuaXF1ZShpc29saW5lcyRpc29saW5lKSkgewogIHBhaXJzID0gaXNvbGluZXNbaXNvbGluZXMkaXNvbGluZSA9PSBpc29saW5lLCAicGFpciJdCiAgCiAgcGFpciA9IHBhaXJzWzFdCiAgZG93bkdlbmVzRmlsZSA9IHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfZG93bnJlZ3VsYXRlZF9nZW5lcy5jc3YiKQogIGRvd25HZW5lcyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KGRvd25HZW5lc0ZpbGUsIHNlcCA9ICIsIiwgaGVhZGVyID0gVFJVRSwgcm93Lm5hbWVzID0gMSkpCiAgZG93bkdlbmVzIDwtIHJvd25hbWVzKGRvd25HZW5lcykKICAKICBpZiAobGVuZ3RoKHBhaXJzKSA+IDEpIHsKICAgIGZvciAocGFpciBpbiBwYWlyc1syOmxlbmd0aChwYWlycyldKSB7CiAgICAgIGRvd25HZW5lc0ZpbGUgPSBzdHJfaW50ZXJwKCJkZXNlcS9vdXRwdXQvJHtwYWlyfV9zaWduaWZpY2FudGx5X2Rvd25yZWd1bGF0ZWRfZ2VuZXMuY3N2IikKICAgICAgbmV3RG93bkdlbmVzIDwtIGFzLmRhdGEuZnJhbWUocmVhZC5jc3YoZG93bkdlbmVzRmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICAgICAgZG93bkdlbmVzIDwtIGludGVyc2VjdChkb3duR2VuZXMsIHJvd25hbWVzKG5ld0Rvd25HZW5lcykpCiAgICB9CiAgfQogIAogIHVwc2V0TGlzdElucHV0W1tpc29saW5lXV0gPSBkb3duR2VuZXMKfQoKdXBzZXQoZnJvbUxpc3QodXBzZXRMaXN0SW5wdXQpLCBuc2V0cyA9IDcsIG5pbnRlcnNlY3RzID0gNTAsIG9yZGVyLmJ5ID0gImRlZ3JlZSIpCgojIE91dHB1dCB0aGUgbGlzdHMgb2YgZ2VuZXMgY29ycmVzcG9uZGluZyB0byBlYWNoIGNvbHVtbiBvZiB0aGUgdXBzZXQgcGxvdApkb3ducmVnVXBzZXRQbG90R2VuZXMuZGYgPC0gZ2V0VXBzZXRQbG90RGF0YSh1cHNldExpc3RJbnB1dCkKd3JpdGUuY3N2KGRvd25yZWdVcHNldFBsb3RHZW5lcy5kZiwgZmlsZSA9ICJkZXNlcS9vdXRwdXQvdXBzZXRfcGxvdF9pc29saW5lc19saXN0c19zaWduaWZpY2FudGx5X2Rvd25yZWd1bGF0ZWRfZ2VuZXNfYWxsX3N1YmxpbmVzLmNzdiIpCnVwcmVnVXBzZXRQbG90R2VuZXMuZGYKYGBgCgoKIyMjIFVwcmVndWxhdGVkIHBhdGh3YXkgdXBzZXQgcGxvdAoKU2hvd3Mgb25seSB0aGUgdXByZWd1bGF0ZWQgcGF0aHdheXMuCgpgYGB7ciB1cHJlZyBwYXRoIHVwc2V0IHBsb3R9CnVwcmVnVXBzZXRMaXN0SW5wdXQgPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpKQpmb3IgKHBhaXIgaW4gc2Vuc2l0aXZlX3Jlc2lzdGFudF9wYWlycykgewogIHNwbGl0IDwtIHN0cnNwbGl0KHBhaXIsICJfdnNfIikKICBjb250IDwtIHNwbGl0W1sxXV1bMl0KICBleHAgPC0gc3BsaXRbWzFdXVsxXQogIAogIHVwUGF0aHdheXNGaWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke2V4cH1fdnNfJHtjb250fV9zaWduaWZpY2FudGx5X3VwcmVndWxhdGVkX3BhdGh3YXlzLmNzdiIpCiAgdXBQYXRod2F5cyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KHVwUGF0aHdheXNGaWxlLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpKQogIGtleSA9IHN0cl9pbnRlcnAoIiR7ZXhwfV9zaWdfdXByZWdfcGF0aHdheXMiKQogIHVwcmVnVXBzZXRMaXN0SW5wdXRbW2tleV1dID0gdXBQYXRod2F5cyREZXNjcmlwdGlvbgp9Cgp1cHNldChmcm9tTGlzdCh1cHJlZ1Vwc2V0TGlzdElucHV0KSwgbnNldHMgPSA3LCBuaW50ZXJzZWN0cyA9IDUwLCBvcmRlci5ieSA9ICJkZWdyZWUiKQpgYGAKCk91dHB1dCB0aGUgbGlzdHMgb2YgcGF0aHdheXMgY29ycmVzcG9uZGluZyB0byBlYWNoIGNvbHVtbiBvZiB0aGUgdXBzZXQgcGxvdAoKYGBge3IgdXByZWd1bGF0ZWQgcGF0aCB1cHNldCBwbG90IGRhdGF9CmZyb21MaXN0KHVwcmVnVXBzZXRMaXN0SW5wdXQpCnVwcmVnVXBzZXRQbG90UGF0aHdheXMuZGYgPC0gZ2V0VXBzZXRQbG90RGF0YSh1cHJlZ1Vwc2V0TGlzdElucHV0KQp3cml0ZS5jc3YodXByZWdVcHNldFBsb3RQYXRod2F5cy5kZiwgZmlsZSA9ICJkZXNlcS9vdXRwdXQvdXBzZXRfcGxvdF9saXN0c19zaWduaWZpY2FudGx5X3VwcmVndWxhdGVkX3BhdGh3YXlzLmNzdiIpCnVwcmVnVXBzZXRQbG90UGF0aHdheXMuZGYKYGBgCgojIyMgRG93bnJlZ3VsYXRlZCBwYXRod2F5IHVwc2V0IHBsb3QKClNob3dzIG9ubHkgdGhlIGRvd25yZWd1bGF0ZWQgcGF0aHdheXMuCgpgYGB7ciBkb3ducmVnIHBhdGggdXBzZXQgcGxvdH0KZG93bnJlZ1Vwc2V0TGlzdElucHV0IDwtIHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoPWxlbmd0aChzZW5zaXRpdmVfcmVzaXN0YW50X3BhaXJzKSkKZm9yIChwYWlyIGluIHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpIHsKICBzcGxpdCA8LSBzdHJzcGxpdChwYWlyLCAiX3ZzXyIpCiAgY29udCA8LSBzcGxpdFtbMV1dWzJdCiAgZXhwIDwtIHNwbGl0W1sxXV1bMV0KICAKICBkb3duUGF0aHdheXNGaWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke2V4cH1fdnNfJHtjb250fV9zaWduaWZpY2FudGx5X2Rvd25yZWd1bGF0ZWRfcGF0aHdheXMuY3N2IikKICBkb3duUGF0aHdheXMgPC0gYXMuZGF0YS5mcmFtZShyZWFkLmNzdihkb3duUGF0aHdheXNGaWxlLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpKQogIGtleSA9IHN0cl9pbnRlcnAoIiR7ZXhwfV9zaWdfZG93bnJlZ19wYXRod2F5cyIpCiAgZG93bnJlZ1Vwc2V0TGlzdElucHV0W1trZXldXSA9IGRvd25QYXRod2F5cyREZXNjcmlwdGlvbgp9Cgp1cHNldChmcm9tTGlzdChkb3ducmVnVXBzZXRMaXN0SW5wdXQpLCBuc2V0cyA9IDcsIG5pbnRlcnNlY3RzID0gNTAsIG9yZGVyLmJ5ID0gImRlZ3JlZSIpCmBgYAoKT3V0cHV0IHRoZSBsaXN0cyBvZiBwYXRod2F5cyBjb3JyZXNwb25kaW5nIHRvIGVhY2ggY29sdW1uIG9mIHRoZSB1cHNldCBwbG90CgpgYGB7ciBkb3ducmVndWxhdGVkIHBhdGggdXBzZXQgcGxvdCBkYXRhfQpmcm9tTGlzdChkb3ducmVnVXBzZXRMaXN0SW5wdXQpCmRvd25yZWdVcHNldFBsb3RQYXRod2F5cy5kZiA8LSBnZXRVcHNldFBsb3REYXRhKGRvd25yZWdVcHNldExpc3RJbnB1dCkKd3JpdGUuY3N2KGRvd25yZWdVcHNldFBsb3RQYXRod2F5cy5kZiwgZmlsZSA9ICJkZXNlcS9vdXRwdXQvdXBzZXRfcGxvdF9saXN0c19zaWduaWZpY2FudGx5X2Rvd25yZWd1bGF0ZWRfcGF0aHdheXMuY3N2IikKZG93bnJlZ1Vwc2V0UGxvdFBhdGh3YXlzLmRmCmBgYAoKUmVhZCBpbiBwbGF0aW51bSBtZWNoYW5pc20gZGF0YQpgYGB7cn0KcGxhdGludW1HZW5lRGF0YSA8LSBhcy5kYXRhLmZyYW1lKHJlYWQudGFibGUoImRlc2VxL3NwZWNpZmljLXBhdGh3YXlzL3BsYXRpbnVtLWxpc3QtYWxsLW1lY2hhbmlzbXMudHh0Iiwgc2VwID0gIlxuIiwgaGVhZGVyID0gVFJVRSkpCnJvdy5uYW1lcyhwbGF0aW51bUdlbmVEYXRhKSA8LSBwbGF0aW51bUdlbmVEYXRhWywxXQoKcGxhdGludW1HZW5lRGF0YQpgYGAKCiMjIFZpc3VhbGl6aW5nIGRpZmZlcmVudGlhbCBnZW5lcyBhY3Jvc3Mgc2Vuc2l0aXZlL3Jlc2lzdGFudCBwYWlycwoKU2hvd3MgZ2VuZSBleHByZXNzaW9uIGFjcm9zcyBzZW5zaXRpdmUvcmVzaXN0YW50IHBhaXJzLiBJbmNsdWRlcyBnZW5lcyB0aGF0IGFyZSBjb25zaXN0ZW50bHkgcmVndWxhdGVkIGluIGF0IGxlYXN0IDMgc2Vuc2l0aXZlL3Jlc2lzdGFudCBwYWlycy4gVXNlcyBhIG1lYW4tY2VudGVyZWQgYXBwcm9hY2guCgpgYGB7cn0KY29uc2lzdGVudGx5UmVndWxhdGVkR2VuZXMgPSByb3duYW1lcyhmdWxsX3Jlc3VsdHNfZ2VuZXNbZnVsbF9yZXN1bHRzX2dlbmVzJG51bV9wYWlyc19zaWdfcmVndWxhdGVkID4gMiwgXSkKCmRkcy5hbGwgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudG1hdHJpeC5hbGwsIGNvbERhdGEgPSBtZXRhZGF0YS5hbGwsIGRlc2lnbiA9IH4gQ2VsbExpbmUgKyBSZXBsaWNhdGUpCgphbm5vdGF0aW9uX2NvbCA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEoZGRzLmFsbClbLGMoIlJlc2lzdGFudCIsICJJc29MaW5lIildKQphbm5vdGF0aW9uX2NvbCRSZXNpc3RhbnQgPC0gYXMuZmFjdG9yKGFubm90YXRpb25fY29sJFJlc2lzdGFudCkKYW5ub3RhdGlvbl9jb2wkSXNvTGluZSA8LSBhcy5mYWN0b3IoYW5ub3RhdGlvbl9jb2wkSXNvTGluZSkKYW5ub3RhdGlvbl9jb2wgPC0gYXMuZGF0YS5mcmFtZShhbm5vdGF0aW9uX2NvbCkKCnNlbGVjdCA8LSBUUE0ubG9nW3Jvd25hbWVzKFRQTS5sb2cpICVpbiUgY29uc2lzdGVudGx5UmVndWxhdGVkR2VuZXMsXQoKbWF0IDwtIGFzLm1hdHJpeChzZWxlY3QpCm1hdCA8LSBtYXQgLSByb3dNZWFucyhtYXQpCnBoZWF0bWFwKG1hdCwKICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogIGNsdXN0ZXJfY29scyA9IEZBTFNFLAogIHNob3dfcm93bmFtZXMgPSBGQUxTRSwKICBsYWJlbHNfY29sID0gY29sbmFtZXMoc2VsZWN0KSwKICBhbm5vdGF0aW9uX2NvbCA9IGFubm90YXRpb25fY29sLAogIGxlZ2VuZCA9IFRSVUUsCiAgY2VsbHdpZHRoID0gNSwKICBmb250c2l6ZSA9IDUsCiAgZm9udHNpemVfcm93ID0gMiwKICBib3JkZXJfY29sb3I9TkEKKQpgYGA=